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