C#多线程开发-使用并发集合

Posted dotNET跨平台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#多线程开发-使用并发集合相关的知识,希望对你有一定的参考价值。

前言

大家好,我是阿辉。

在C#语言中当需要处理并发的场景时,就需要程序员使用最合理的数据结构。那么哪些数据结构是支持和可以在并行计算中被使用的呢。

首先这些数据结构具备可伸缩性,尽可能地避免锁(会造成多个线程的等待,防止资源竞争),同时还能提供线程安全的访问。

在.NET Framework4.0中引入了System.Collections.Concurrent命名空间,其中就包含几个数据结构。

  • ConcurrentQueus

  • ConcurrentDictionary

  • ConcurrentStack

  • ConcurrentBag

  • BlockingCollect

那么接下来,我们来看看这些支持并行计算的数据结构到底应该如何使用!

ConcurrentQueus

该集合使用了原子的比较和交换(CAS),以及SpinWait来保持线程安全。

它实现了一个先进先出(First In First Out简称FIFO)的集合。元素的出队列和加入队列的顺序是一致的。

Enqueus(): 向队列中加入元素。

TryDequeus(): 取出队列中的第一个元素,此时队里无此元素。

TryPeek(): 得到第一个元素但并不从队列中删除该元素。

ConcurrentStack

此集合在实现层也没有加入任何锁,只采用了CAS操作。

它是一个后进后出(Last In First Out,简称LIFO)集合,最近添加的元素先出去(类比为栈)。

Push()和PushRange(): 给该集合添加元素。

TryPop()和TryPopRange(): 从该集合获取元素。

TryPeek(): 检查元素。

ConcurrentBag

该集合是一个支持重复元素的无序集合,专门针对下面多个线程工作时,集合进行了优化。每个线程产生和消费自己的任务,极少与其他线程的任何交互(若需要使用交互,则必须使用锁操作)。

Add(): 添加元素。

TryPeek(): 检查元素。

TryTake(): 获取元素。

ConcurrentDictionary

是一个线程安全的字典集合。其中读操作无需使用锁,写操作需要使用锁。

该并发字典使用多个锁,在字典之上实现一个细粒度的锁模型。其中使用concurrencyLevel可以在构造函数中定义锁的数量,那么说意味着预估的线程数量将并发地更新该字典。

由于并发字典使用锁,所以一些操作需要获取该字典中的所有锁。若是在编程过程中,没有必要则不要调用下面方法:Count,IsEmpty,Keys,Values,CopyTo及ToArray。

BlockingCollection

该集合是对泛型接口IProducerConsumerCollection实现的一个高级封装。其中有很多管道场景,即当你有一些操作需要使用之前计算的结果。

BlockingCollection支持如下功能:

  • 分块

  • 调整内部集合容量

  • 取消集合操作

  • 从多个块集合中获取元素

Demo

在单线程的环境中使用通用字典与使用通用字典的性能。

使用ConcurrentDictionary

class Program
    
        const string Item = "";
        public static string CurrentItem;
        static void Main(string[] args)
        
            var concurrentDicrionary=new ConcurrentDictionary<int ,string>();
            var dictionary=new Dictionary<int ,string>();

            var sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < 1000000; i++)
            
                lock(dictionary)
                
                    dictionary[i]=Item;
                
            
            sw.Stop();
            Console.WriteLine("写dictionary的时间"+sw.Elapsed);

            sw.Restart();
            for (int i = 0; i < 1000000; i++)
            
                concurrentDicrionary[i] = Item;
            
            sw.Stop();
            Console.WriteLine("写并发集合concurrentDicrionary的时间:" + sw.Elapsed);

            sw.Restart();
            for (int i = 0; i < 1000000; i++)
            
                lock(dictionary)
                
                    CurrentItem = dictionary[i];
                
            
            sw.Stop();
            Console.WriteLine("读dictionary的时间" + sw.Elapsed);

            sw.Restart();
            for (int i = 0; i < 1000000; i++)
            
                CurrentItem=concurrentDicrionary[i];
            
            sw.Stop();
            Console.WriteLine("读并发集合concurrentDicrionary的时间:" + sw.Elapsed);

            Console.ReadKey();
        
    

可以发现使用ConcurrentDictionary写操作比使用锁的通用字典要慢很多,而读操作则更快些。因此如果对字典需要大量的线程安全的读操作,则ConcurrentDictionary是更好的选择。

小寄语

人生短暂,我不想去追求自己看不见的,我只想抓住我能看得见的。

原创不易,给个关注。

我是阿辉,感谢您的阅读,如果对你有帮助,麻烦点赞、转发  谢谢。

往期推荐

C#多线程开发-线程间通讯

C#多线程开发-处理子线程中的异常

C#多线程开发-了解C#5.0 05

C#多线程开发-任务并行库04

C#多线程开发-线程池03

C#多线程开发-线程同步02

C#多线程开发-线程基础 01

以上是关于C#多线程开发-使用并发集合的主要内容,如果未能解决你的问题,请参考以下文章

c# 并发编程系列之三:使用 Parallel 开始第1个多线程编码

JAVA并发七(多线程环境中安全使用集合API)

Java并发编程:多线程环境中安全使用集合API(含代码)

List集合多线程并发条件下不安全,如何解决?

java - 并发集合 VectorsynchronizedCollectionCopyOnWriteArrayList之间的区别。

转:Java并发编程之八:多线程环境中安全使用集合API(含代码)