• 线程安全


    1、线程安全集合

    BlockingCollection:一个支持界限和阻塞的容器(线程安全集合),与队列结构相似,常用函数如下

    Add :向容器中插入元素

    TryTake:从容器中取出元素并删除

    TryPeek:从容器中取出元素,但不删除。

    CompleteAdding:告诉容器,添加元素完成。此时如果还想继续添加会发生异常。

    IsCompleted:告诉消费线程,生产者线程还在继续运行中,任务还未完成。

    普通用法示例:

    /// <summary>
    /// 线程安全集合用法
    /// </summary>
    public static void BC()
    {
        //线程安全集合
        using (BlockingCollection<int> blocking = new BlockingCollection<int>())
        {
            int NUMITEMS = 10000;
    
            for (int i = 1; i < NUMITEMS; i++)
            {
                blocking.Add(i);
            }
            //完成添加
            blocking.CompleteAdding();
    
            int outerSum = 0;
    
            // 定义一个委托方法取出集合元素
            Action action = () =>
            {
                int localItem;
                int localSum = 0;
    
                //取出并删除元素,先进先出
                while (blocking.TryTake(out localItem))
                {
                    localSum += localItem;
                }
                //两数相加替换第一个值
                Interlocked.Add(ref outerSum, localSum);
            };
    
            //并行3个线程执行,多个线程同时取集合的数据
            Parallel.Invoke(action, action, action);
    
            Console.WriteLine($"0+...{NUMITEMS-1} = {((NUMITEMS * (NUMITEMS - 1)) / 2)},输出结果:{outerSum}");
            //此集合是否已标记为已完成添加且为空
            Console.WriteLine($"线程安全集合.IsCompleted={blocking.IsCompleted}");
        }
    }

     限制集合长度示例

    /// <summary>
    /// 限制集合长度
    /// </summary>
    public static void BCLimtLength()
    {
        //限制集合长度为5个,后面进的会阻塞等集合少于5个再进来
        BlockingCollection<int> blocking = new BlockingCollection<int>(5);
    
        var task1= Task.Run(() =>
        {
            for (int i = 0; i < 20; i++)
            {
                blocking.Add(i);
                Console.WriteLine($"集合添加:({i})");
            }
    
            blocking.CompleteAdding();
            Console.WriteLine("完成添加");
        });
    
        // 延迟500ms执行等待先生产数据
        var task2 = Task.Delay(500).ContinueWith((t) =>
        {
            while (!blocking.IsCompleted)
            {
                var n = 0;
                if (blocking.TryTake(out n))
                {
                    Console.WriteLine($"取出:({n})");
                }
            }
    
            Console.WriteLine("IsCompleted = true");
        });
    
        Task.WaitAll(task1, task2);
    }

     在BlockingCollection中使用Stack(栈,先进后出)示例:

    /// <summary>
    /// 线程安全集合,先进后出
    /// </summary>
    public static void BCStack()
    {
        //线程安全集合,参数表明栈标识,队列长度为5
        BlockingCollection<int> blocking = new BlockingCollection<int>(new ConcurrentStack<int>(), 5);
    
        var task1 = Task.Run(() =>
        {
            for (int i = 0; i < 20; i++)
            {
                blocking.Add(i);
                Console.WriteLine($"集合添加:({i})");
            }
    
            blocking.CompleteAdding();
            Console.WriteLine("完成添加");
        });
    
        // 等待先生产数据
        var task2 = Task.Delay(500).ContinueWith((t) =>
        {
            while (!blocking.IsCompleted)
            {
                var n = 0;
                if (blocking.TryTake(out n))
                {
                    Console.WriteLine($"取出:({n})");
                }
            }
    
            Console.WriteLine("IsCompleted = true");
        });
    
        Task.WaitAll(task1, task2);
    }

    2、线程安全字典

    ConcurrentDictionary :这个比较好理解,普通字典多线程并发时添加时会报错,而这个则是线程安全的,不会报错。

    普通字典示例:

    //普通字典
    private static IDictionary<string, string> Dictionaries { get; set; } = new Dictionary<string, string>();
    /// <summary>
    /// 字典增加值
    /// </summary>
    public static void AddDictionaries()
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        //并发1000个线程写
        Parallel.For(0, 1000, (i) =>
        {
            var key = $"key-{i}";
            var value = $"value-{i}";
    
            // 不加锁会报错
           // lock (Dictionaries)
           // {
                Dictionaries.Add(key, value);
           // }
        });
        sw.Stop();
        Console.WriteLine("Dictionaries 当前数据量为: {0}", Dictionaries.Count);
        Console.WriteLine("Dictionaries 执行时间为: {0} ms", sw.ElapsedMilliseconds);
    }  

    不加锁时结果:

     加锁后:

     线程安全字典示例:

    //线程安全字典
    private static IDictionary<string, string> ConcurrentDictionaries { get; set; } = new ConcurrentDictionary<string, string>();
    
    / <summary>
    /// 线程安全字典添加值
    /// </summary>
    public static void AddConcurrentDictionaries()
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        //并发1000个线程写
        Parallel.For(0, 1000, (i) =>
        {
            var key = $"key-{i}";
            var value = $"value-{i}";
    
            // 无须加锁
            ConcurrentDictionaries.Add(key, value);
            
        });
        sw.Stop();
        Console.WriteLine("ConcurrentDictionaries 当前数据量为: {0}", ConcurrentDictionaries.Count);
        Console.WriteLine("ConcurrentDictionaries 执行时间为: {0} ms", sw.ElapsedMilliseconds);
    }

     可以看到,线程安全字典比普通字典性能略好,且线程安全字典无需加锁。

    1

  • 相关阅读:
    加载时间分析与优化
    t
    linux 3389
    切片声明 切片在内存中的组织方式 reslice
    从模版生成 uri Golang 的 html/template 包不太适合于这种情况
    负载均衡实现,一个域名对应多个IP地址
    京东首页 淘宝首页 图片加载 单域名 多域名 图片服务
    Reduce DNS Lookups 减少DNS查找
    Make Fewer HTTP Requests 减少HTTP请求
    What the 80/20 Rule Tells Us about Reducing HTTP Requests
  • 原文地址:https://www.cnblogs.com/lili9696189/p/16179353.html
Copyright © 2020-2023  润新知