go 互斥鎖實現原理

2022-09-27 23:06:24 字數 2096 閱讀 3443

目錄2. 加解鎖過程

3. 自旋過程

4. mutex模式

5. woken狀態

6. 為什麼重複解鎖要panic

go中通過mutex來實現對互斥資源的鎖定

go type mutex struce
下圖展示了mutex的記憶體布局

協程之間搶鎖的過程實際上是給locked賦值1的過程,能給locked賦值為1,表示搶鎖成功,搶不到鎖就阻塞等待sema訊號量來喚醒加鎖

mutex對外提供兩個方法

假定當前只有乙個協程在加鎖,沒有其他協程干擾,加鎖過程如下

加鎖過程會去判斷locked是否為1,如果是0則把locked置為1,表示加鎖成功,其他狀態位不會發生變化

假定加鎖時,鎖被其他協程占用,那麼加鎖過程如下:

如果協程對乙個已經被占用的協程加鎖時,waiter計數器會增加1,此時b將會阻塞,直到locked變為0後才會喚醒

假定解鎖時,沒有其他協程阻塞,那麼解鎖過程如下

由於此時watier值為0,表示沒有其他協程在等待,所以無須釋放訊號量,只要把locked置為0即可

假定解鎖過程,有1個或多個協程阻塞,那麼此時的解鎖過程

協程a解鎖分為兩個步驟

將locked置為0

釋放sema訊號量,喚醒協程b,並將waiter減1

此時locked為0,協程b收到訊號量,將locked置為1,b獲得鎖

​ 加鎖時,如果當前locked位為1, 說明該鎖被其他協程占用,但嘗試加鎖的協程並不會馬上轉為阻塞狀態,而是會持續的檢測locked位是否為0,這個過程稱為自旋。

自旋的過程很短,如果在自旋過程中發現鎖被釋放,那麼該協程會立即獲得鎖,被喚醒的協程會繼續阻塞

自旋的好處是,當加鎖失敗時,不必立即轉入阻塞,有一定機會獲得鎖,避免了協程之間的切換

​ 自旋對應cpu指令"pause"(暫停,停頓),cpu對該指令什麼都不做,相當於cpu空轉,對程式而要相當於sleep了一段時間,該時間非常短,當前為30個時鐘週期

自旋過程會持續檢測locked是否為0,它不同於sleep,不需要協程轉為睡眠狀態

自旋的優勢是更充分的利用了cpu,避免了協程切換。因為當前申**鎖的協程獲得了cpu,如果通過短時間自旋就可以獲得鎖,那麼就可以直接執行,而不用阻塞並切換協程

如果自旋過程中獲得了鎖,那麼之前阻塞的協程就無法獲得鎖。如果等待加鎖的協程特別多,而都在自旋過程中獲得了鎖,那麼之前阻塞的協程就將一直阻塞。

為了解決這個問題,在1.8的版本之後增加了乙個狀態starving。在這個狀態下不會自旋,一定會有乙個協程被喚醒並加鎖

現在我們看下starving位的作用

每個mutex都有兩個模式,稱為normal和starving。

預設情況下都是normal模式

當乙個協程加鎖失敗時,不會立即轉入等待狀態,而是判斷是否滿足自旋條件,如果滿足,則自旋來等待鎖

自旋模式搶到鎖,表示有協程釋放了鎖,我們知道釋放鎖時,如果waiter>0,即有阻塞等待的協程,會釋放訊號量來喚醒協程,當協程被喚醒後,發現locked=1,鎖又被搶占,則又會阻塞,但在阻塞前會判斷自上次阻塞到本次阻塞經歷了多長時間,如果超過1ms的話,會將mutex標記為"飢餓"模式,然後再阻塞

在飢餓模式下,不會啟動自旋,如果有協程釋放鎖,那麼一定會喚醒乙個協程,被喚醒的協程會獲得鎖,同時會把waiter減1.

woken狀態作用於加鎖和解鎖的過程中,如果乙個協程正在解鎖,另乙個協程在自旋等待加鎖,那麼會把woken狀態置為1,通知解鎖的協程不用釋放訊號量。

unlock()過程分為將locked置為0,然後判斷waiter是否大於0,如果大於0就釋放訊號量

如果多次unlock(),則可能會喚醒多個協程,多個協程喚醒後會繼續在lock()的邏輯裡搶鎖,勢必會增加lock()實現的複雜度,也會引起不必要的協程切換。

GO 互斥鎖實現原理剖析

互斥鎖是併發程式中對共享資源進行訪問控制的主要手段,對此go語言提供了非常簡單易用的mutex,mutex為一結構體型別,對外暴露兩個方法lock 和unlock 分別用於加鎖和解鎖。mutex使用起來非常方便,但其內部實現卻複雜得多,這包括mutex的幾種狀態。另外,我們也想 一下mutex重複解...

Go 互斥鎖和讀寫互斥鎖的實現

目錄 先來看這樣一段 所存在的問題 var wg sync.waitgroup var x int64 func main func f wg.done 這裡為什麼輸出是 12135 不同的機器結果不一樣 而不是20000。因為 x 的賦值,總共分為三個步驟 取出x的值 計算x的結果 給x賦值。那麼...

執行緒同步與互斥 實現互斥鎖

今天我們來分享一下,執行緒同步與互斥 互斥鎖的實現。多個執行緒同時訪問共享資料時可能會產生衝突,造成程式執行結果不是我們所預期的結果。不產生衝突的多執行緒訪問情況,和截圖如下 產生衝突的多執行緒訪問情況,和截圖如下 注 每執行一次,結果都可能會不同。由於多執行緒訪問共享資料時可能會產生衝突,不能保證...