Golang之Mutex

引用

  1. sync.mutex 源代碼分析
  2. Golang 中 Mutex 的源碼實(shí)現(xiàn)

建議閱讀

  1. Golang中Mutex的實(shí)現(xiàn)
  2. 圖解Go里面的互斥鎖mutex了解編程語(yǔ)言核心實(shí)現(xiàn)源碼

互斥鎖有兩種狀態(tài):正常狀態(tài)和饑餓狀態(tài)。
在正常狀態(tài)下,所有等待鎖的goroutine按照FIFO順序等待。喚醒的goroutine不會(huì)直接擁有鎖,而是會(huì)和新請(qǐng)求鎖的goroutine競(jìng)爭(zhēng)鎖的擁有。新請(qǐng)求鎖的goroutine具有優(yōu)勢(shì):它正在CPU上執(zhí)行,而且可能有好幾個(gè),所以剛剛喚醒的goroutine有很大可能在鎖競(jìng)爭(zhēng)中失敗。在這種情況下,這個(gè)被喚醒的goroutine會(huì)加入到等待隊(duì)列的前面。 如果一個(gè)等待的goroutine超過(guò)1ms沒(méi)有獲取鎖,那么它將會(huì)把鎖轉(zhuǎn)變?yōu)轲囸I模式。
在饑餓模式下,鎖的所有權(quán)將從unlock的gorutine直接交給交給等待隊(duì)列中的第一個(gè)。新來(lái)的goroutine將不會(huì)嘗試去獲得鎖,即使鎖看起來(lái)是unlock狀態(tài), 也不會(huì)去嘗試自旋操作,而是放在等待隊(duì)列的尾部。
如果一個(gè)等待的goroutine獲取了鎖,并且滿足一以下其中的任何一個(gè)條件:(1)它是隊(duì)列中的最后一個(gè);(2)它等待的時(shí)候小于1ms。它會(huì)將鎖的狀態(tài)轉(zhuǎn)換為正常狀態(tài)。
正常狀態(tài)有很好的性能表現(xiàn),饑餓模式也是非常重要的,因?yàn)樗茏柚刮膊垦舆t的現(xiàn)象。

原子操作:指那些不能夠被打斷的操作被稱為原子操作,當(dāng)有一個(gè)CPU在訪問(wèn)這塊內(nèi)容addr時(shí),其他CPU就不能訪問(wèn)。

CAS:比較及交換,其實(shí)也屬于原子操作,但它是非阻塞的,所以在被操作值被頻繁變更的情況下,CAS操作并不那么容易成功,不得不利用for循環(huán)以進(jìn)行多次嘗試。

自旋鎖(spinlock)
自旋鎖是指當(dāng)一個(gè)線程在獲取鎖的時(shí)候,如果鎖已經(jīng)被其他線程獲取,那么該線程將循環(huán)等待,然后不斷地判斷是否能夠被成功獲取,知直到獲取到鎖才會(huì)退出循環(huán)。獲取鎖的線程一直處于活躍狀態(tài) Golang中的自旋鎖用來(lái)實(shí)現(xiàn)其他類型的鎖,與互斥鎖類似,不同點(diǎn)在于,它不是通過(guò)休眠來(lái)使進(jìn)程阻塞,而是在獲得鎖之前一直處于活躍狀態(tài)(自旋)。

Mutex結(jié)構(gòu)

type Mutex struct {
    state int32  // 表示鎖當(dāng)前的狀態(tài)
    sema  uint32 // 信號(hào)量 用于向處于Gwaitting的G發(fā)送信號(hào)
}

狀態(tài)值[2]

  • mutexLocked
    值為 1,第一位為 1,表示 mutex 已經(jīng)被加鎖。根據(jù) mutex.state & mutexLocked 的結(jié)果來(lái)判斷 mutex 的狀態(tài):該位為 1 表示已加鎖,0 表示未加鎖。

  • mutexWoken
    值為 2,第二位為 1,表示 mutex 是否被喚醒。根據(jù) mutex.state & mutexWoken 的結(jié)果判斷 mutex 是否被喚醒:該位為 1 表示已被喚醒,0 表示未被喚醒。

  • mutexStarving
    值為 4,第三位為 1,表示 mutex 是否處于饑餓模式。根據(jù) mutex.state & mutexWoken 的結(jié)果判斷 mutex 是否處于饑餓模式:該位為 1 表示處于饑餓模式,0 表示正常模式。

  • mutexWaiterShift
    值為 3,表示 mutex.state 右移 3 位后即為等待的 goroutine 的數(shù)量。

  • starvationThresholdNs
    值為 1000000 納秒,即 1ms,表示將 mutex 切換到饑餓模式的等待時(shí)間閾值。

工作模式[2]

  • 正常模式下
    等待者以 FIFO 的順序排隊(duì)來(lái)獲取鎖,但被喚醒的等待者發(fā)現(xiàn)并沒(méi)有獲取到 mutex,并且還要與新到達(dá)的 goroutine 們競(jìng)爭(zhēng) mutex 的所有權(quán)。新到達(dá)的 goroutine 們有一個(gè)優(yōu)勢(shì) —— 它們已經(jīng)運(yùn)行在 CPU 上且可能數(shù)量很多,所以一個(gè)醒來(lái)的等待者有很大可能會(huì)獲取不到鎖。在這種情況下它處在等待隊(duì)列的前面。如果一個(gè) goroutine 等待 mutex 釋放的時(shí)間超過(guò) 1ms,它就會(huì)將 mutex 切換到饑餓模式。

  • 在饑餓模式下
    mutex 的所有權(quán)直接從對(duì) mutex 執(zhí)行解鎖的 goroutine 傳遞給等待隊(duì)列前面的等待者。新到達(dá)的 goroutine 們不要嘗試去獲取 mutex,即使它看起來(lái)是在解鎖狀態(tài),也不要試圖自旋(等也白等,在饑餓模式下是不會(huì)給你的),而是自己乖乖到等待隊(duì)列的尾部排隊(duì)去。

如果一個(gè)等待者獲得 mutex 的所有權(quán),并且看到以下兩種情況中的任一種:1) 它是等待隊(duì)列中的最后一個(gè),或者 2) 它等待的時(shí)間少于 1ms,它便將 mutex 切換回正常操作模式。

正常模式有更好地性能,因?yàn)橐粋€(gè) goroutine 可以連續(xù)獲得好幾次 mutex,即使有阻塞的等待者。而饑餓模式可以有效防止出現(xiàn)位于等待隊(duì)列尾部的等待者一直無(wú)法獲取到 mutex 的情況。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容