程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> .NET 4.0新增命名空間:System.Collections.Concurrent

.NET 4.0新增命名空間:System.Collections.Concurrent

編輯:關於.NET

集合類的需求總是源源不斷,因此,不管是1.0到2.0的泛型,還是3.0到4.0 的並行(本文的並行指Concurrent,非 Parallel),.NET每個版本總會伴隨著一些 集合類的增長。由於並行計算現在已經越來越流行,這裡我將對.NET 4.0中新增 的命名空間System.Collections.Concurrent和它下面的類做一些簡單的介紹。

為什麼需要Concurrent?

相信不少朋友都有多線程編程的經歷吧,不過在.NET 4.0以前,多線程編程 下很容易出問題,先看個簡單的例子吧。

static void main()
{
myList = new List<string>();
for (int i = 0; i < 1000; i++)
{
myList.Add(i.ToString());
}
new Thread(T2).Start();
new Thread(T3).Start();
}
static IList<string> myList;
static void T2()
{
Thread.Sleep(100);
for (int i = 0; i < 50; i++)
{
myList.Remove(i.ToString());
}
}
static void T3()
{
foreach (var a in myList)
{
Console.WriteLine(a);
}
}

在這個例子中,我們首先初始化一個長度為1000數組,然後我們開啟兩個線 程,一個進行刪除操作,另外個則進行簡單的讀操作。運行代碼的話你會發現程 序會拋出InvalidOperationException,因為系統集合在被讀的同時被修改了,因 此列舉操作可能不能執行。

當然在4.0以前我們也有辦法避免這種類似的操作,比如我們可以對要操作的 對象加鎖。即我們可以在對myList集合進行讀寫操作之前對其添加代碼lock (myList)。

然而這種方式畢竟不夠簡潔,並且在更復雜的情況下它可能會顯得非常繁瑣 。這時候支持並行操作的集合應運而生了。

有哪些Concurrent集合?

在System.Collections.Concurrent公開的類並不多,他們分別在兩個不同的 dll中存在,其中在System.dll中僅僅一個BlockingCollection<T>和 ConcurrentBag<T>,在mscorlib.dll中則稍多一些,他們分別是 ConcurrentQueue<T>, ConcurrentStack<T>, ConcurrentDictionary<TKey, TValue>。不過對於日常開發來說,他們基 本夠用。下面我們來一個個看下這些類的構造吧。

首先看看mscorlib中的幾個類,這些類其實我們在.NET 2.0中已經接觸過它 們的普通版本,因此它們功能基本不變,因此你仍然可以像以前那樣使用普通版 本的哈希表,隊列和堆棧來使用它們。

不過並行類不僅僅簡單以前的集合類改造成線程安全的並行類,它同時還提 供了一些更豐富的功能,由於Lamda表達式的引入,現在在 ConcurrentDictionary<TKey, TValue>你可以通過AddOrUpdate或 GetOrAdd添加自己的值生成方案。這使得我們在生成鍵值對的時候更加方便和簡 單了。如:

ConcurrentDictionary<int, string> td = new  ConcurrentDictionary<int, string>();
Func<int, string> genVar = (i) => i.ToString();
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 1000; i++)
{
td.GetOrAdd(i, genVar);
}
});
Task.Factory.StartNew(() =>
{
Func<int, string, string> updateVar = (key, oldVar)  => oldVar + key;
td.AddOrUpdate(0, genVar, updateVar);
Console.WriteLine(td[0]);
}).Wait();

我們可以看到,現在我們在取值的時候,如果哈希表中沒有該值的話我們可 以自己生成,或者我們在添加新的值的時候如果碰到重復鍵值的時候也可以很方 便的解決掉了。相對以前在添加或者查找數據時痛苦的判斷,這種方式更加簡潔 和方便。

其他幾個類相對來說函數使用變化不大,唯一區別比較大的也是在 Concurrent命名空間中很常見的各種try函數操作。

接著我們來看看在System.dll中的並行集合,這裡面的兩個類在以前的.NET 中是沒有的。首先看看 ConcurrentBag<T>,顧名思義,這個類提供並行 數據包的功能,這個類相對來說構造比較簡單,它繼承自四個接口: IProducerConsumerCollection<T>, IEnumerable<T>, ICollection, IEnumerable,後面三個集合類常用的接口我們當然很熟悉,而且 由於實現了IEnumerable<T>接口,ConcurrentBag<T>也支持LINQ操 作。不過這裡有個特殊的接口 IProducerConsumerCollection<T>我們以 前沒有見過,雖然是個新的接口,但故名思意,這個接口提供了生產者/消費者 的集合操作,它提供了四個基本的方法:CopyTo(T[],int), ToArray(), TryAdd(T), TryTake(out T)。其中我們主要關注後面的兩個方法,TryAdd是添 加元素操作,而TryTake則是取元素操作。不過在ConcurrentBag 中,TryAdd方 法被設置為protected,外部對象需要通過Add操作來添加元素。另外,取元素的 話,除了TryTake之外,我們還可以通過 TryPeek來取集合當前的最後一個元素 而不刪除它。下面來看看例子:

static void main()
{
ConcurrentBag<string> bag = new  ConcurrentBag<string>();
Task.Factory.StartNew(() =>
{
for (int i = 0; i < 1000; i++)
{
bag.Add(i.ToString());
}
bag.Add("Last");
});
Task.Factory.StartNew(() =>
{
foreach (string item in bag)
{
Console.WriteLine(item);
}
}).Wait();
} 

BlockingCollection<T>相對來說,要稍微復雜一些,它實現了四個接 口:IEnumerable<T>, ICollection, IEnumerable, IDisposable,因此 該集合同樣支持LINQ,不僅如此,它也提供了比ConcurrentBag更加豐富的功能 。如 CompleteAdding和超時設置的TryAdd和TryTake方法等等。

總結

隨著多線程和並行編程的要求越來越多,相信在未來,.NET家族新增的這些 類將會在我們的日常編程生活中越來越常見,所以掌握它們也顯得越來越又必要 了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved