每次寫部落格,第一句話都是這樣的:程式設計師很苦逼,除了會寫程式,還得會寫部落格!
當然,題外話說多了,咱進入正題!
背景
基於任務的程式設計、命令式資料並行和任務並行都要求能夠支援併發更新的陣列、列表和集合。
在.net framework 4 以前,為了讓共享的陣列、列表和集合能夠被多個執行緒更新,需要新增複雜的**來同步這些更新操作。
如您需要編寫乙個並行迴圈,這個迴圈以無序的方式向乙個共享集合中新增元素,那麼必須加入乙個同步機制來保證這是乙個執行緒安全的集合。
system.collenctions和system.collenctions.generic 命名空間中所提供的經典列表、集合和陣列的執行緒都不是安全的,不能接受併發請求,因此需要對相應的操作方法執行序列化。
下面看**,**中並沒有實現執行緒安全和序列化:
class programview code/*coder:天才臥龍 * **中 建立三個併發執行緒 來操作_products 集合 * system.collections.generic.list 這個列表在多個執行緒訪問下,不能保證是安全的執行緒,所以不能接受併發的請求,我們必須對add方法的執行進行序列化 */ static void main(string args) ); /*建立任務 t2 t2 執行 資料集合新增操作*/ task t2 = task.factory.startnew(() => ); /*建立任務 t3 t3 執行 資料集合新增操作*/ task t3 = task.factory.startnew(() => ); task.waitall(t1, t2, t3); console.writeline(_products.count); console.readline(); } /*執行集合資料新增操作*/ static void addproducts() ); } } class product public string category public int sellprice }
**中開啟了三個併發操作,每個操作都向集合中新增1000條資料,在沒有保障線程安全和序列化的執行下,實際得到的資料並沒有3000條,結果如下:
為此我們需要採用lock關鍵字,來確保每次只有乙個執行緒來訪問 _products.add(product); 這個方法,**如下:
class programview code/*coder:天才臥龍 * **中 建立三個併發執行緒 來操作_products 集合 * system.collections.generic.list 這個列表在多個執行緒訪問下,不能保證是安全的執行緒,所以不能接受併發的請求,我們必須對add方法的執行進行序列化 */ static void main(string args) ); /*建立任務 t2 t2 執行 資料集合新增操作*/ task t2 = task.factory.startnew(() => ); /*建立任務 t3 t3 執行 資料集合新增操作*/ task t3 = task.factory.startnew(() => ); task.waitall(t1, t2, t3); console.writeline("當前資料量為:" + _products.count); console.readline(); } /*執行集合資料新增操作*/ static void addproducts() }); } } class product public string category public int sellprice }
但是鎖的引入,帶來了一定的開銷和效能的損耗,並降低了程式的擴充套件性,在併發程式設計中顯然不適用。
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 是完全無鎖的,能夠支援併發的新增元素,先進先出。下面貼**,詳解見注釋:
class programprivate static concurrentqueue_concurrenproducts /*coder:天才臥龍 * **中 建立三個併發執行緒 來操作_products 和 _concurrenproducts 集合,每次新增 10000 條資料 檢視 一般佇列queue 和 多執行緒安全下的佇列concurrentqueue 執**況 */ static void main(string args) ); /*建立任務 t2 t2 執行 資料集合新增操作*/ task t2 = task.factory.startnew(() => ); /*建立任務 t3 t3 執行 資料集合新增操作*/ task t3 = task.factory.startnew(() => ); task.waitall(t1, t2, t3); swtask.stop(); console.writeline("list當前資料量為:" + _products.count); console.writeline("list執行時間為:" + swtask.elapsedmilliseconds); thread.sleep(1000); _concurrenproducts = new concurrentqueue(); stopwatch swtask1 = new stopwatch(); swtask1.start(); /*建立任務 tk1 tk1 執行 資料集合新增操作*/ task tk1 = task.factory.startnew(() => ); /*建立任務 tk2 tk2 執行 資料集合新增操作*/ task tk2 = task.factory.startnew(() => ); /*建立任務 tk3 tk3 執行 資料集合新增操作*/ task tk3 = task.factory.startnew(() => ); task.waitall(tk1, tk2, tk3); swtask1.stop(); console.writeline("concurrentqueue當前資料量為:" + _concurrenproducts.count); console.writeline("concurrentqueue執行時間為:" + swtask1.elapsedmilliseconds); console.readline(); } /*執行集合資料新增操作*/ static void addproducts() { parallel.for(0, 30000, (i) => { product product = new product(); product.name = "name" + i;
併發及併發的執行緒安全處理
目錄 執行緒安全性 原子性提供了互斥訪問,同一時刻只有乙個執行緒可以來對它操作 原子包 具有原子性,執行緒安全的,atomicint 原始碼實現unsafe類的getandaddint實現原理 迴圈判斷當前的值和主記憶體值是否一致,相等就加一,用到的算 法cas全稱compareandswapint...
併發安全和鎖
有時候在go 中可能會存在多個goroutine同時操作乙個資源,這種情況會發生資料競態問題。舉例 var x int64 var wg sync.waitgroup func add wg.done func main 上面的 中開啟了 2 個goroutine去累加變數x的值,這 2 個goro...
多執行緒 安全併發
同乙個,同時,多個 則執行緒會不安全 synchronized鎖物件,不是鎖方法 目標不對鎖定失敗,選好要鎖的物件,不是提款機,是提款人 public synchronized void test public void run 顧客 static class customer implements...