亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Golang?Mutex?原理詳細解析

 更新時間:2022年08月31日 09:05:02   作者:ag9920  
這篇文章主要介紹了Golang?Mutex原理詳細解析,文章圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考價值,需要的小伙伴可以參考一下

前言

互斥鎖是在并發(fā)程序中對共享資源進行訪問控制的主要手段。對此 Go 語言提供了簡單易用的 Mutex。Mutex 和 Goroutine 合作緊密,概念容易混淆,一定注意要區(qū)分各自的概念。

Mutex 是一個結(jié)構(gòu)體,對外提供 Lock()Unlock()兩個方法,分別用來加鎖和解鎖。

// A Locker represents an object that can be locked and unlocked.
type Locker interface {
    Lock()
    Unlock()
}

type Mutex struct {
    state int32 
    sema  uint32
}

const (
	mutexLocked = 1 << iota // mutex is locked
	mutexWoken
	mutexStarving
	mutexWaiterShift = iota
)
  • Mutex 是一個互斥鎖,其零值對應(yīng)了未上鎖的狀態(tài),不能被拷貝;
  • state 代表互斥鎖的狀態(tài),比如是否被鎖定;
  • sema 表示信號量,協(xié)程阻塞會等待該信號量,解鎖的協(xié)程釋放信號量從而喚醒等待信號量的協(xié)程。

注意到 state 是一個 int32 變量,內(nèi)部實現(xiàn)時把該變量分成四份,用于記錄 Mutex 的狀態(tài)。

  • Locked: 表示該 Mutex 是否已經(jīng)被鎖定,0表示沒有鎖定,1表示已經(jīng)被鎖定;
  • Woken: 表示是否有協(xié)程已經(jīng)被喚醒,0表示沒有協(xié)程喚醒,1表示已經(jīng)有協(xié)程喚醒,正在加鎖過程中;
  • Starving: 表示該 Mutex 是否處于饑餓狀態(tài),0表示沒有饑餓,1表示饑餓狀態(tài),說明有協(xié)程阻塞了超過1ms;

上面三個表示了 Mutex 的三個狀態(tài):鎖定 - 喚醒 - 饑餓。

Waiter 信息雖然也存在 state 中,其實并不代表狀態(tài)。它表示阻塞等待鎖的協(xié)程個數(shù),協(xié)程解鎖時根據(jù)此值來判斷是否需要釋放信號量。

協(xié)程之間的搶鎖,實際上爭搶給Locked賦值的權(quán)利,能給 Locked 置為1,就說明搶鎖成功。搶不到就阻塞等待 sema 信號量,一旦持有鎖的協(xié)程解鎖,那么等待的協(xié)程會依次被喚醒。

Woken 和 Starving 主要用于控制協(xié)程間的搶鎖過程。

Lock

func (m *Mutex) Lock() {
	// Fast path: grab unlocked mutex.
	if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
		if race.Enabled {
			race.Acquire(unsafe.Pointer(m))
		}
		return
	}
	// Slow path (outlined so that the fast path can be inlined)
	m.lockSlow()
}

若當(dāng)前鎖已經(jīng)被使用,請求 Lock() 的 goroutine 會阻塞,直到鎖可用為止。

單協(xié)程加鎖

若只有一個協(xié)程加鎖,無其他協(xié)程干擾,在加鎖過程中會判斷 Locked 標(biāo)志位是否為 0,若當(dāng)前為 0 則置為 1,代表加鎖成功。這里本質(zhì)是一個 CAS 操作,依賴了 atomic.CompareAndSwapInt32。

加鎖被阻塞

假設(shè)協(xié)程B在嘗試加鎖前,已經(jīng)有一個協(xié)程A獲取到了鎖,此時的狀態(tài)為:

此時協(xié)程B嘗試加鎖,被阻塞,Mutex 的狀態(tài)為:

Waiter 計數(shù)器增加了1,協(xié)程B將會持續(xù)阻塞,直到 Locked 值變成0 后才會被喚醒。

Unlock

func (m *Mutex) Unlock() {
	if race.Enabled {
		_ = m.state
		race.Release(unsafe.Pointer(m))
	}

	// Fast path: drop lock bit.
	new := atomic.AddInt32(&m.state, -mutexLocked)
	if new != 0 {
		// Outlined slow path to allow inlining the fast path.
		// To hide unlockSlow during tracing we skip one extra frame when tracing GoUnblock.
		m.unlockSlow(new)
	}
}

如果 Mutex 沒有被加鎖,就直接 Unlock ,會拋出一個 runtime error。

從源碼注釋來看,一個 Mutex 并不會與某個特定的 goroutine 綁定,理論上講用一個 goroutine 加鎖,另一個 goroutine 解鎖也是允許的,不過為了代碼可維護性,一般還是建議不要這么搞。

A locked Mutex is not associated with a particular goroutine. It is allowed for one goroutine to lock a Mutex and then arrange for another goroutine to unlock it.

無協(xié)程阻塞下的解鎖

假定在解鎖時,沒有其他協(xié)程阻塞等待加鎖,那么只需要將 Locked 置為 0 即可,不需要釋放信號量。

解鎖并喚醒協(xié)程

假定解鎖時有1個或多個協(xié)程阻塞,解鎖過程分為兩個步驟:

  • Locked位置0;
  • 看到 Waiter > 0,釋放一個信號量,喚醒一個阻塞的協(xié)程,被喚醒的協(xié)程把 Locked 置為1,獲取到鎖。

自旋

加鎖時,如果當(dāng)前 Locked 位為1,則說明當(dāng)前該鎖由其他協(xié)程持有,嘗試加鎖的協(xié)程并不是馬上轉(zhuǎn)入阻塞,而是會持續(xù)探測 Locked 位是否變?yōu)?,這個過程就是「自旋」。

自旋的時間很短,如果在自旋過程中發(fā)現(xiàn)鎖已經(jīng)被釋放,那么協(xié)程可以立即獲取鎖。此時即便有協(xié)程被喚醒,也無法獲取鎖,只能再次阻塞。

自旋的好處是,當(dāng)加鎖失敗時不必立即轉(zhuǎn)入阻塞,有一定機會獲取到鎖,這樣可以避免一部分協(xié)程的切換。

什么是自旋

自旋對應(yīng)于 CPU 的 PAUSE 指令,CPU 對該指令什么都不做,相當(dāng)于空轉(zhuǎn)。對程序而言相當(dāng)于sleep了很小一段時間,大概 30個時鐘周期。連續(xù)兩次探測Locked 位的間隔就是在執(zhí)行這些 PAUSE 指令,它不同于sleep,不需要將協(xié)程轉(zhuǎn)為睡眠態(tài)。

自旋條件

加鎖時 Golang 的 runtime 會自動判斷是否可以自旋,無限制的自旋將給 CPU 帶來巨大壓力,自旋必須滿足以下所有條件:

  • 自旋次數(shù)要足夠少,通常為 4,即自旋最多 4 次;
  • CPU 核數(shù)要大于 1,否則自旋沒有意義,因為此時不可能有其他協(xié)程釋放鎖;
  • 協(xié)程調(diào)度機制中的 P 的數(shù)量要大于 1,比如使用 GOMAXPROCS() 將處理器設(shè)置為 1 就不能啟用自旋;
  • 協(xié)程調(diào)度機制中的可運行隊列必須為空,否則會延遲協(xié)程調(diào)度。

可見自旋的條件是很苛刻的,簡單說就是不忙的時候才會啟用自旋。

自旋的優(yōu)勢

自旋的優(yōu)勢是更充分地利用 CPU,盡量避免協(xié)程切換。因為當(dāng)前申請加鎖的協(xié)程擁有 CPU,如果經(jīng)過短時間的自旋可以獲得鎖,則當(dāng)前寫成可以繼續(xù)運行,不必進入阻塞狀態(tài)。

自旋的問題

如果在自旋過程中獲得鎖,那么之前被阻塞的協(xié)程就無法獲得。如果加鎖的協(xié)程特別多,每次都通過自旋獲取鎖,則之前被阻塞的協(xié)程將很難獲取鎖,從而進入【饑餓狀態(tài)】。

為此,Golang 1.8 版本后為Mutex增加了Starving模式,在這個狀態(tài)下不會自旋,一旦有協(xié)程釋放鎖。那么一定會喚醒一個協(xié)程并成功加鎖。

Mutex 的模式

每個 Mutex 都有兩種模式:NormalStarving。

Normal 模式

默認情況下的模式就是 Normal。 在該模式下,協(xié)程如果加鎖不成功,不會立即轉(zhuǎn)入阻塞排隊(先進先出),而是判斷是否滿足自旋條件,如果滿足則會啟動自旋過程,嘗試搶鎖。

Starving 模式

自旋過程中能搶到鎖,一定意味著同一時刻有協(xié)程釋放了鎖。我們知道釋放鎖時,如果發(fā)現(xiàn)有阻塞等待的協(xié)程,那么還會釋放一個信號量來喚醒一個等待協(xié)程,被喚醒的協(xié)程得到 CPU 后開始運行,此時發(fā)現(xiàn)鎖已經(jīng)被搶占了,自己只好再次阻塞,不過阻塞前會判斷,自上次阻塞到本次阻塞經(jīng)過了多長時間,如果超過 1ms,則會將 Mutex 標(biāo)記為 Starving模式,然后阻塞。

Starving模式下,不會啟動自旋過程,一旦有協(xié)程釋放了鎖,一定會喚醒協(xié)程,被喚醒的協(xié)程將成功獲取鎖,同時會把等待計數(shù)減 1。

Woken 狀態(tài)

Woken 狀態(tài)用于加鎖和解鎖過程中的通信。比如,同一時刻,兩個協(xié)程一個在加鎖,一個在解鎖,在加鎖的協(xié)程可能在自旋過程中,此時把 Woken 標(biāo)記為 1,用于通知解鎖協(xié)程不必釋放信號量,類似知會一下對方,不用釋放了,我馬上就拿到鎖了。

到此這篇關(guān)于Golang Mutex 原理詳細解析的文章就介紹到這了,更多相關(guān)Golang Mutex 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • golang獲取變量或?qū)ο箢愋偷膸追N方式總結(jié)

    golang獲取變量或?qū)ο箢愋偷膸追N方式總結(jié)

    在golang中并沒有提供內(nèi)置函數(shù)來獲取變量的類型,但是通過一定的方式也可以獲取,下面這篇文章主要給大家介紹了關(guān)于golang獲取變量或?qū)ο箢愋偷膸追N方式,需要的朋友可以參考下
    2022-12-12
  • 深入探究Golang中flag標(biāo)準庫的使用

    深入探究Golang中flag標(biāo)準庫的使用

    在本文中,我們將深入探討 flag 標(biāo)準庫的實現(xiàn)原理和使用技巧,以幫助讀者更好地理解和掌握該庫的使用方法,文中的示例代碼講解詳細,感興趣的可以了解一下
    2023-04-04
  • golang實現(xiàn)ftp實時傳輸文件的案例

    golang實現(xiàn)ftp實時傳輸文件的案例

    這篇文章主要介紹了golang實現(xiàn)ftp實時傳輸文件的案例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • 用golang實現(xiàn)一個定時器任務(wù)隊列實例

    用golang實現(xiàn)一個定時器任務(wù)隊列實例

    golang中提供了2種定時器timer和ticker,分別是一次性定時器和重復(fù)任務(wù)定時器。這篇文章主要介紹了用golang實現(xiàn)一個定時器任務(wù)隊列實例,非常具有實用價值,需要的朋友可以參考下
    2018-05-05
  • Go 簡單實現(xiàn)多租戶數(shù)據(jù)庫隔離

    Go 簡單實現(xiàn)多租戶數(shù)據(jù)庫隔離

    本文主要介紹了Go 簡單實現(xiàn)多租戶數(shù)據(jù)庫隔離,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • Go框架三件套Gorm?Kitex?Hertz基本用法與常見API講解

    Go框架三件套Gorm?Kitex?Hertz基本用法與常見API講解

    這篇文章主要為大家介紹了Go框架三件套Gorm?Kitex?Hertz的基本用法與常見API講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪<BR>
    2023-02-02
  • 使用go net實現(xiàn)簡單的redis通信協(xié)議

    使用go net實現(xiàn)簡單的redis通信協(xié)議

    本文主要介紹了go net實現(xiàn)簡單的redis通信協(xié)議,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • golang游戲等資源壓縮包創(chuàng)建和操作方法

    golang游戲等資源壓縮包創(chuàng)建和操作方法

    這篇文章主要介紹了golang游戲等資源壓縮包創(chuàng)建和操作,本文通過示例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08
  • Golang filepath包常用函數(shù)詳解

    Golang filepath包常用函數(shù)詳解

    本文介紹與文件路徑相關(guān)包,該工具包位于path/filepath中,該包試圖與目標(biāo)操作系統(tǒng)定義的文件路徑兼容。本文介紹一些常用函數(shù),如獲取文件絕對路徑,獲取文件名或目錄名、遍歷文件、分割文件路徑、文件名模式匹配等函數(shù),并給具體示例進行說明
    2023-02-02
  • go?doudou開發(fā)gRPC服務(wù)快速上手實現(xiàn)詳解

    go?doudou開發(fā)gRPC服務(wù)快速上手實現(xiàn)詳解

    這篇文章主要為大家介紹了go?doudou開發(fā)gRPC服務(wù)快速上手實現(xiàn)過程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12

最新評論