每次寫部落格,第一句話都是這樣的:程式員很苦逼,除了會寫程式,還得會寫部落格!
當然,題外話說多了,咱進入正題!
背景
基于任務的程式設計、指令式資料并行和任務并行都要求能夠支援并發更新的數組、清單和集合。
在.NET Framework 4 以前,為了讓共享的數組、清單和集合能夠被多個線程更新,需要添加複雜的代碼來同步這些更新操作。
如您需要編寫一個并行循環,這個循環以無序的方式向一個共享集合中添加元素,那麼必須加入一個同步機制來保證這是一個線程安全的集合。
System.Collenctions和System.Collenctions.Generic 名稱空間中所提供的經典清單、集合和數組的線程都不是安全的,不能接受并發請求,是以需要對相應的操作方法執行串行化。
下面看代碼,代碼中并沒有實作線程安全和串行化:

View Code
代碼中開啟了三個并發操作,每個操作都向集合中添加1000條資料,在沒有保障線程安全和串行化的運作下,實際得到的資料并沒有3000條,結果如下:
為此我們需要采用Lock關鍵字,來確定每次隻有一個線程來通路 _Products.Add(product); 這個方法,代碼如下:

但是鎖的引入,帶來了一定的開銷和性能的損耗,并降低了程式的擴充性,在并發程式設計中顯然不适用。
System.Collections.Concurrent
.NET Framework 4提供了新的線程安全和擴充的并發集合,它們能夠解決潛在的死鎖問題和競争條件問題,是以在很多複雜的情形下它們能夠使得并行代碼更容易編寫,這些集合盡可能減少需要使用鎖的次數,進而使得在大部分情形下能夠優化為最佳性能,不會産生不必要的同步開銷。
需要注意的是:
線程安全并不是沒有代價的,比起System.Collenctions和System.Collenctions.Generic命名空間中的清單、集合和數組來說,并發集合會有更大的開銷。是以,應該隻在需要從多個任務中并發通路集合的時候才使用并發幾個,在串行代碼中使用并發集合是沒有意義的,因為它們會增加無謂的開銷。
為此,在.NET Framework中提供了System.Collections.Concurrent新的命名空間可以通路用于解決線程安全問題,通過這個命名空間能通路以下為并發做好了準備的集合。
1.BlockingCollection 與經典的阻塞隊列資料結構類似,能夠适用于多個任務添加和删除資料,提供阻塞和限界能力。
2.ConcurrentBag 提供對象的線程安全的無序集合
3.ConcurrentDictionary 提供可有多個線程同時通路的鍵值對的線程安全集合
4.ConcurrentQueue 提供線程安全的先進先出集合
5.ConcurrentStack 提供線程安全的後進先出集合
這些集合通過使用比較并交換和記憶體屏障等技術,避免使用典型的互斥重量級的鎖,進而保證線程安全和性能。
ConcurrentQueue
ConcurrentQueue 是完全無鎖的,能夠支援并發的添加元素,先進先出。下面貼代碼,詳解見注釋:
