C 執行緒鎖(上)

2021-09-08 06:55:33 字數 2570 閱讀 2972

本篇從monitor,mutex,manualresetevent,autoresetevent,waithandler的類關係圖開始,希 望通過本篇的介紹能對常見的執行緒同步方法有乙個整體的認識,而對每種方式的使用細節,適用場合不會過多解釋。讓我們來看看這幾個類的關係圖:

1.lock關鍵字

msdn上給出了使用lock時的注意事項通常,應避免鎖定 public 型別,否則例項將超出**的控制範圍。常見的結構 lock (this)、lock (typeof (mytype)) 和 lock ("mylock") 違反此準則。

1)如果例項可以被公共訪問,將出現 lock (this) 問題。

2)如果 mytype 可以被公共訪問,將出現 lock (typeof (mytype)) 問題由於乙個類的所有例項都只有乙個型別物件(該物件是typeof的返回結果),鎖定它,就鎖定了該物件的所有例項。微軟現在建議不要使用 lock(typeof(mytype)),因為鎖定型別物件是個很緩慢的過程,並且類中的其他執行緒、甚至在同乙個應用程式域中執行的其他程式都可以訪問 該型別物件,因此,它們就有可能代替您鎖定型別物件,完全阻止您的執行,從而導致你自己的**的掛起。

3)由於程序中使用同一字串的任何其他**將共享同乙個鎖,所以出現 lock(「mylock」) 問題。這個問題和.net framework建立字串的機制有關係,如果兩個string變數值都是"mylock",在記憶體中會指向同一字串物件。

最佳做法是定義 private 物件來鎖定, 或 private static物件變數來保護所有例項所共有的資料。

我們再來通過il dasm看看lock關鍵字的本質,下面是一段簡單的測試**:

lock

(lockobject)

用il dasm開啟編譯後的檔案,上面的語句塊生成的il**為:

il_0045:

call

void [mscorlib]system.threading.

monitor

::enter

(object)

il_004a:

nop.try

// end .try

finally

// end handler

通過上面的**我們很清楚的看到:lock關鍵字其實就是對monitor類的enter()和exit()方法的封裝,並通過try...catch...finally語句塊確保在lock語句塊結束後執行monitor.exit()方法,釋放互斥鎖。

2.monitor類

monitor類通過向單個執行緒授予物件鎖來控制對物件的訪問。物件鎖提供限制訪問臨界區的能力。當乙個執行緒擁有物件的鎖時,其他任何 執行緒都不能獲取該鎖。還可以使用 monitor 來確保不會允許其他任何執行緒訪問正在由鎖的所有者執行的應用程式**節,除非另乙個執行緒正在使用其他的鎖定物件執行該**。

通過對lock關鍵字的分析我們知道,lock就是對monitor的enter和exit的乙個封裝,而且使用起來更簡潔,因此monitor類的enter()和exit()方法的組合使用可以用lock關鍵字替代。

另外monitor類還有幾個常用的方法:

tryenter()能夠有效的解決長期死等的問題,如果在乙個併發經常發生,而且持續時間長的環境中使用tryenter,可以有效防止死鎖或者長時間 的等待。比如我們可以設定乙個等待時間bool gotlock = monitor.tryenter(myobject,1000),讓當前執行緒在等待1000秒後根據返回的bool值來決定是否繼續下面的操作。

wait()釋放物件上的鎖以便允許其他執行緒鎖定和訪問該物件。在其他執行緒訪問物件時,呼叫執行緒將等待。脈衝訊號用於通知等待執行緒有關物件狀態的更改。

pulse(),pulseall()向乙個或多個等待執行緒傳送訊號。該訊號通知等待執行緒鎖定物件的狀態已更改,並且鎖的所有者準備釋放該鎖。等待執行緒被 放置在物件的就緒佇列中以便它可以最後接收物件鎖。一旦執行緒擁有了鎖,它就可以檢查物件的新狀態以檢視是否達到所需狀態。

注意:pulse、pulseall和wait方法必須從同步的**塊內呼叫。

我們假定一種情景:媽媽做蛋糕,小孩有點饞,媽媽每做好一塊就要吃掉,媽媽做好一塊後,告訴小孩蛋糕已經做好了。下面的例子用monitor類的wait和pulse方法模擬小孩吃蛋糕的情景。

//僅僅是說明wait和pulse/pulseall的例子

//邏輯上並不嚴密,使用場景也並不一定合適

class

monitorsample}}

public

void

consume()}}

static

void

main(

string

args)}

這個例子的目的是要理解wait和pulse如何保證執行緒同步的,同時要注意wait(obeject)和wait(object,int)方法的區別,理解它們的區別很關鍵的一點是要理解同步的物件包含若干引用,其中包括對當前擁有鎖的執行緒的引用、對就緒佇列(包含準備獲取鎖的執行緒)的引用和對等待佇列(包含等待物件狀態更改通知的執行緒)的引用。

排程器開鎖上鎖

給排程器上鎖函式osschedlock 程式清單l3.9 用於禁止任務排程,直到任務完成後呼叫給排程器開鎖函式osschedunlock 為止 程式清單l3.10 呼叫osschedlock 的任務保持對cpu的控制權,儘管有個優先順序更高的任務進入了就緒態。然而,此時中斷是可以被識別的,中斷服務也...

併發程式設計 讀寫鎖上鎖流程

目錄 併發程式設計之讀寫鎖上鎖流程 一 寫鎖上鎖流程 二 讀鎖上鎖流程 寫鎖的上鎖流程 slf4j topic liheng public class rwlock2 finally t1 t1.start 寫鎖在加鎖的時候要麼鎖沒有被人持有則會成功,要麼鎖是重入 否則都失敗 protected f...

談談基於Redis分布式鎖(上) 手寫方案

單體架構的應用可以直接使用本地鎖 synchronized 就可以解決多執行緒資源競爭的問題。如果公司業務發展較快,可以通過部署多個服務節點來提高系統的並行處理能力。由於本地鎖的作用範圍只限於當前應用的執行緒。高併發場景下,集群中某個應用的本地鎖並不會對其它應用的資源訪問產生互斥,就會產生資料不一致...