- A+
.Net 提供了基于生产-消费模式的集合类,这些集合对多线程访问安全,定义在System.Collections.Concurrent名称空间中。这个名称空间中包括基础接口IProduceConsumerCollection
- BlockingCollection
- ConcurrentBag
- ConcurentDictionary<TKey,TValue>
- ConcurrentQueue
- ConcurentStack
在使用生产-消费模式时,我们经常使用两个线程,在一个线程向集合添加数据,在另一个线程从集合中提取数据进行处理。我们可以使用实现IProduceConsumerCollection
下面是BlockingCollection的示例。这个集合类的Take方法可以从集合中获取并去掉一个对象,当集合为空时,可以使线程处于阻塞状态。
Console.WriteLine("--------------------------------"); Console.WriteLine("测试一个线程向集合添加数据,另一个线程读取数据,请输入人名,输入exit退出"); BlockingCollection<string> names=new BlockingCollection<string>(); Task.Run(() => { while (true) { var name = names.Take(); Console.WriteLine("你好,"+name); } }); var name = Console.ReadLine(); while (name!="exit") { if(!string.IsNullOrEmpty(name)) names.Add(name); name = Console.ReadLine(); }
BlockingCollection的另一个功能是可以封装其它的IProduceConsumerCollection
using System.Collections.Concurrent; Console.WriteLine("--------------------------------"); Console.WriteLine("测试BlockingCollection 和 ConcurrentQueue"); var queue = new ConcurrentQueue<string>(); var blockqueue= new BlockingCollection<string>(queue, 100); Console.WriteLine("加入name1"); blockqueue.Add("name1"); Console.WriteLine("加入name2"); blockqueue.Add("name2"); Console.WriteLine("加入name3"); blockqueue.Add("name3"); Console.WriteLine(blockqueue.Take()); Console.WriteLine(blockqueue.Take()); Console.WriteLine(blockqueue.Take()); Console.WriteLine("--------------------------------"); Console.WriteLine("测试BlockingCollection 和 ConcurrentStack"); var cq = new ConcurrentStack<string>(); var bc = new BlockingCollection<string>(cq, 100); Console.WriteLine("加入name1"); bc.Add("name1"); Console.WriteLine("加入name2"); bc.Add("name2"); Console.WriteLine("加入name3"); bc.Add("name3"); Console.WriteLine(bc.Take()); Console.WriteLine(bc.Take()); Console.WriteLine(bc.Take());
ConcurrentBag需要特别说明一下,在“纯生产-消费”场景中(一个线程要么向集合添加项目,要么从集合中获取项目,但不能即添加又获取),ConcurrentBag性能要比其他类型的集合慢,但在“混合生产-消费”场景中(一个线程即可以向集合添加项目,也可以获取项目),ConcurrentBag的性能要比其它类型的集合快。