每次寫部落格,第一句話都是這樣的:程式員很苦逼,除了會寫程式,還得會寫部落格!當然,希望将來的一天,某位老闆看到此部落格,給你的程式員職工加點薪資吧!因為程式員的世界除了苦逼就是沉默。我眼中的程式員大多都不愛說話,默默承受着程式設計的巨大壓力,除了技術上的交流外,他們不願意也不擅長和别人交流,更不樂意任何人走進他們的内心!
最近悟出來一個道理,在這兒分享給大家:學曆代表你的過去,能力代表你的現在,學習代表你的将來。我們都知道計算機技術發展日新月異,速度驚人的快,你我稍不留神,就會被慢慢淘汰!是以:每日不間斷的學習是避免被淘汰的不二法寶。
當然,題外話說多了,咱進入正題!
背景
C#命名空間:System.Collenctions和System.Collenctions.Generic 中提供了很多清單、集合和數組。例如:List<T>集合,數組Int[],String[] ......,Dictory<T,T>字典等等。但是這些清單、集合和數組的線程都不是安全的,不能接受并發請求。下面通過一個例子來加以說明,如下:
本例中,開辟了三個線程,通過循環向集合中添加資料,每個線程執行1000次(三個線程之間的操作是同時進行的,也是并行的),那麼,理論上結果應該是3000。
上文中我們講到: C#命名空間:System.Collenctions和System.Collenctions.Generic 下的清單,數組,集合并不能保證線程安全,并不能防止并發的發生。
本例運作的結果也證明了上述結論的正确性,其結果如下:
由此可見:C#命名空間:System.Collenctions和System.Collenctions.Generic 下的清單,數組,集合确實不能保證線程安全,确實不能預防并發。那麼我們應當怎麼解決上述問題呢?
還好,自C#2.0以來,LOCK是一直存在的。使用LOCK(互斥鎖)是可以做到防止并發的,示例代碼如下:
引入了Lock,運作結果也正常了,如下:
但是鎖的引入,帶來了一定的開銷和性能的損耗,并降低了程式的擴充性,而且還會有死鎖的發生(雖說機率不大,但也不能不防啊),是以:使用LOCK進行并發程式設計顯然不太适用。
還好,微軟一直在更新自己的東西:
.NET Framework 4提供了新的線程安全和擴充的并發集合,它們能夠解決潛在的死鎖問題和競争條件問題,是以在很多複雜的情形下它們能夠使得并行代碼更容易編寫,這些集合盡可能減少使用鎖的次數,進而使得在大部分情形下能夠優化為最佳性能,不會産生不必要的同步開銷。
需要注意的是:在串行代碼中使用并發集合是沒有意義的,因為它們會增加無謂的開銷。
在.NET Framework4.0以後的版本中提供了命名空間:System.Collections.Concurrent 來解決線程安全問題,通過這個命名空間,能通路以下為并發做好了準備的集合。
1.BlockingCollection 與經典的阻塞隊列資料結構類似,能夠适用于多個任務添加和删除資料,提供阻塞和限界能力。
2.ConcurrentBag 提供對象的線程安全的無序集合
3.ConcurrentDictionary 提供可有多個線程同時通路的鍵值對的線程安全集合
4.ConcurrentQueue 提供線程安全的先進先出集合
5.ConcurrentStack 提供線程安全的後進先出集合
這些集合通過使用比較并交換和記憶體屏障等技術,避免使用典型的互斥重量級的鎖,進而保證線程安全和性能。
ConcurrentQueue
ConcurrentQueue 是完全無鎖的,能夠支援并發的添加元素,先進先出。下面貼代碼,詳解見注釋:
結果如下:
從執行時間上來看,使用 ConcurrentQueue 相比 LOCK 明顯快了很多!
上面的執行個體可以使用ConcurrentBag嗎?當然是可以的啦,因為:ConcurrentBag 和 ConcurrentQueue一樣,操作的對象都是集合,隻不過方式不同罷了!同理:小虎斑們也可以嘗試使用 ConcurrentStack 在這裡,我僅僅貼上使用ConcurrentBag的代碼,如下:
執行結果如下:
對于并發下的其他集合,我這邊就不做代碼案列了!如有疑問,歡迎指正!
@陳卧龍的部落格