天天看點

.Net 線程安全集合

介紹.Net 線程安全集合

.Net 提供了基于生産-消費模式的集合類,這些集合對多線程通路安全,定義在System.Collections.Concurrent名稱空間中。這個名稱空間中包括基礎接口IProduceConsumerCollection,這個接口定義了線程安全集合的基本操作。這個名稱空間中還包括常用的集合:

  • BlockingCollection
  • ConcurrentBag
  • ConcurentDictionary<TKey,TValue>
  • ConcurrentQueue
  • ConcurentStack

在使用生産-消費模式時,我們經常使用兩個線程,在一個線程向集合添加資料,在另一個線程從集合中提取資料進行處理。我們可以使用實作IProduceConsumerCollection接口的集合,比如ConcurrentQueue等等。通常将從集合取資料的代碼放在一個無盡循環中,如果集合中沒有資料就繼續循環。很多情況下,我們希望如果集合中沒有資料,這個線程阻塞等待,直到有資料時再繼續。這時我們可以使用BlockingCollection,這個集合提供了Add(添加資料)和Take(阻塞擷取資料)方法。

下面是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集合,實作不同的添加和擷取順序,比如,如果在構造函數中傳入ConcurrentQueue,添加和擷取就與隊列相同——“先進先出”,如果傳入ConcurrentStack,順序就與堆棧相同——“先進後出”,下面是示例代碼:

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

本文來自部落格園,作者:尋找無名的特質,轉載請注明原文連結:https://www.cnblogs.com/zhenl/p/15768806.html