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