.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的性能要比其它類型的集合快。