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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM