一文掌握Go語(yǔ)言并發(fā)編程必備的Mutex互斥鎖
在并發(fā)編程中,我們需要處理多個(gè)線(xiàn)程同時(shí)對(duì)共享資源的訪問(wèn)問(wèn)題。如果不加控制地同時(shí)訪問(wèn)共享資源,就會(huì)導(dǎo)致競(jìng)爭(zhēng)條件(Race Condition)問(wèn)題,從而導(dǎo)致程序出現(xiàn)不可預(yù)知的錯(cuò)誤。為了解決這個(gè)問(wèn)題,Go 語(yǔ)言提供了 sync 包,其中包括 Mutex 互斥鎖、RWMutex 讀寫(xiě)鎖等同步機(jī)制,本篇博客將著重介紹 Mutex 互斥鎖的基本原理。
1. Mutex 互斥鎖的基本概念
Mutex 是 Mutual Exclusion(互斥)的縮寫(xiě),用于保護(hù)共享資源。當(dāng)一個(gè)goroutine 獲取了 Mutex 的鎖之后,其他的 goroutine 就無(wú)法再獲取到這個(gè) Mutex 的鎖,直到這個(gè) goroutine 釋放了這個(gè) Mutex 的鎖,其他 goroutine 才能繼續(xù)嘗試獲取這個(gè) Mutex 的鎖。
Mutex 互斥鎖包含兩個(gè)狀態(tài):鎖定和未鎖定。當(dāng)一個(gè) goroutine 獲取了 Mutex 的鎖,Mutex 就處于鎖定狀態(tài),其他的 goroutine 就只能等待這個(gè) goroutine 釋放這個(gè) Mutex 的鎖,才能再次嘗試獲取這個(gè) Mutex 的鎖。當(dāng)一個(gè) goroutine 釋放了 Mutex 的鎖,Mutex 就處于未鎖定狀態(tài),此時(shí)其他的 goroutine 可以獲取這個(gè) Mutex 的鎖。
Mutex 互斥鎖使用的是二進(jìn)制信號(hào)量的概念,當(dāng) Mutex 處于鎖定狀態(tài)時(shí),就相當(dāng)于信號(hào)量為 0,其他 goroutine 只能等待;當(dāng) Mutex 處于未鎖定狀態(tài)時(shí),就相當(dāng)于信號(hào)量為 1,其他 goroutine 可以嘗試獲取這個(gè) Mutex 的鎖。
2. Mutex 互斥鎖的基本用法
Mutex 互斥鎖的基本用法如下:
package main ? import ( "fmt" "sync" "time" ) ? func main() { var wg sync.WaitGroup var mu sync.Mutex var count int ? for i := 0; i < 10; i++ { wg.Add(1) go func() { mu.Lock() defer mu.Unlock() count++ fmt.Println(count) time.Sleep(time.Millisecond) wg.Done() }() } ? wg.Wait() }
在上面的例子中,我們創(chuàng)建了 10 個(gè) goroutine,每個(gè) goroutine 都會(huì)獲取 Mutex 的鎖,對(duì)共享變量 count 進(jìn)行自增操作,并打印 count 的值。在獲取 Mutex 的鎖后,我們使用 defer 語(yǔ)句來(lái)保證在 goroutine 結(jié)束時(shí)釋放 Mutex 的鎖。最后,我們使用 WaitGroup 來(lái)等待所有 goroutine 結(jié)束。
由于 Mutex 互斥鎖是排他性的,因此在同一時(shí)刻只有一個(gè) goroutine 可以獲取 Mutex 的鎖,其他的 goroutine 只能等待。在上面的例子中,我們通過(guò) Mutex 互斥鎖來(lái)保證了 count 的原子性操作,從而避免了競(jìng)爭(zhēng)條件問(wèn)題。
3. Mutex 互斥鎖的底層實(shí)現(xiàn)
Mutex 互斥鎖的底層實(shí)現(xiàn)使用了操作系統(tǒng)提供的原語(yǔ)(Primitive),如互斥量(Mutex)、臨界區(qū)(Critical Section)等。在不同的操作系統(tǒng)中,Mutex 互斥鎖的底層實(shí)現(xiàn)可能有所不同。
在 Linux 系統(tǒng)中,Mutex 互斥鎖的底層實(shí)現(xiàn)主要使用了 futex(Fast User-space Mutex)機(jī)制。futex 是 Linux 系統(tǒng)提供的一種快速用戶(hù)空間互斥量機(jī)制,可以實(shí)現(xiàn)用戶(hù)空間的原子操作。
Mutex 互斥鎖的底層實(shí)現(xiàn)主要包括兩個(gè)部分:等待隊(duì)列和鎖狀態(tài)。
3.1 等待隊(duì)列
當(dāng)一個(gè) goroutine 嘗試獲取 Mutex 的鎖時(shí),如果 Mutex 已經(jīng)被其他 goroutine 獲取,那么這個(gè) goroutine 就會(huì)進(jìn)入等待隊(duì)列中等待。等待隊(duì)列是一個(gè)鏈表,每個(gè)節(jié)點(diǎn)代表一個(gè)等待 goroutine。
當(dāng)一個(gè) goroutine 釋放 Mutex 的鎖時(shí),會(huì)喚醒等待隊(duì)列中的第一個(gè) goroutine。喚醒的操作主要包括兩個(gè)步驟:
- 將等待隊(duì)列中的第一個(gè)節(jié)點(diǎn)從鏈表中移除,并將其狀態(tài)設(shè)置為可運(yùn)行(Runnable)狀態(tài)。
- 將移除的節(jié)點(diǎn)中的 goroutine 添加到調(diào)度器的可運(yùn)行隊(duì)列中,等待調(diào)度器將其調(diào)度執(zhí)行。
3.2 鎖狀態(tài)
Mutex 互斥鎖的鎖狀態(tài)主要包括兩個(gè)部分:互斥標(biāo)志和持有者標(biāo)志。
互斥標(biāo)志表示 Mutex 的狀態(tài),0 表示未鎖定,1 表示鎖定?;コ鈽?biāo)志的原子操作主要使用了 Compare-and-Swap(CAS)指令。
持有者標(biāo)志表示當(dāng)前持有 Mutex 的 goroutine 的 ID。如果 Mutex 未被任何 goroutine 持有,那么持有者標(biāo)志為 0。持有者標(biāo)志的原子操作主要使用了 Load Linked(LL)和 Store Conditional(SC)指令。
當(dāng)一個(gè) goroutine 嘗試獲取 Mutex 的鎖時(shí),會(huì)先嘗試使用 CAS 指令將互斥標(biāo)志從 0 改為 1。如果 CAS 指令成功,那么這個(gè) goroutine 就獲得了 Mutex 的鎖,并將持有者標(biāo)志設(shè)置為當(dāng)前 goroutine 的 ID。如果 CAS 指令失敗,那么說(shuō)明 Mutex 已經(jīng)被其他 goroutine 獲取,這個(gè) goroutine 就會(huì)進(jìn)入等待隊(duì)列中等待。
當(dāng)一個(gè) goroutine 釋放 Mutex 的鎖時(shí),會(huì)先將持有者標(biāo)志設(shè)置為 0,然后再使用 LL 和 SC 指令將互斥標(biāo)志從 1 改為 0。LL 指令用于加載互斥標(biāo)志的值,SC 指令用于將互斥標(biāo)志的值改為 0。LL 和 SC 指令是原子指令,可以保證操作的原子性。
4. Mutex 互斥鎖的注意事項(xiàng)
在使用 Mutex 互斥鎖時(shí),需要注意以下幾點(diǎn):
4.1 不要將 Mutex 作為函數(shù)或方法的參數(shù)傳遞
Mutex 是一個(gè)結(jié)構(gòu)體類(lèi)型,包含互斥標(biāo)志和持有者標(biāo)志等字段。當(dāng)將 Mutex 作為函數(shù)或方法的參數(shù)傳遞時(shí),會(huì)將 Mutex 的副本傳遞給函數(shù)或方法,而不是原始的 Mutex 實(shí)例。這樣做會(huì)導(dǎo)致不同的 goroutine 使用不同的 Mutex 實(shí)例,從而無(wú)法實(shí)現(xiàn)互斥。
正確的做法是將 Mutex 定義為一個(gè)全局變量,并在多個(gè) goroutine 中共享這個(gè)全局變量。
4.2 不要在獲取 Mutex 的鎖時(shí)阻塞太久
當(dāng)一個(gè) goroutine 嘗試獲取 Mutex 的鎖時(shí),如果 Mutex 已經(jīng)被其他 goroutine 獲取,那么這個(gè) goroutine 就會(huì)進(jìn)入等待隊(duì)列中等待。如果等待時(shí)間過(guò)長(zhǎng),會(huì)導(dǎo)致性能下降。
可以使用 TryLock() 方法嘗試獲取 Mutex 的鎖。TryLock() 方法會(huì)立即返回,如果獲取鎖成功返回 true,否則返回 false。
4.3 不要重復(fù)釋放 Mutex 的鎖
當(dāng)一個(gè) goroutine 釋放 Mutex 的鎖時(shí),如果這個(gè) goroutine 不是 Mutex 的持有者,那么會(huì)導(dǎo)致 panic 異常。因此,在釋放 Mutex 的鎖時(shí),需要確保當(dāng)前 goroutine 是 Mutex 的持有者。
可以使用 defer 語(yǔ)句在獲取 Mutex 的鎖時(shí)自動(dòng)注冊(cè)釋放鎖的操作,以確保在任何情況下都能正確釋放 Mutex 的鎖。
4.4 不要在鎖內(nèi)部執(zhí)行阻塞或耗時(shí)操作
當(dāng)一個(gè) goroutine 持有 Mutex 的鎖時(shí),其他 goroutine 無(wú)法獲取 Mutex 的鎖,從而會(huì)導(dǎo)致阻塞。如果在 Mutex 的鎖內(nèi)部執(zhí)行阻塞或耗時(shí)操作,會(huì)導(dǎo)致其他 goroutine 長(zhǎng)時(shí)間等待,從而影響性能。
可以將阻塞或耗時(shí)操作放到 Mutex 的鎖外部執(zhí)行,以避免阻塞其他 goroutine。
5. 總結(jié)
本文介紹了 Go 語(yǔ)言中的 Mutex 互斥鎖,包括 Mutex 的基本用法、互斥鎖的底層實(shí)現(xiàn)和注意事項(xiàng)。Mutex 是 Go 語(yǔ)言中實(shí)現(xiàn)互斥的重要工具,可以保證多個(gè) goroutine 之間的數(shù)據(jù)訪問(wèn)安全。
在使用 Mutex 時(shí),需要注意避免一些常見(jiàn)的錯(cuò)誤,如將 Mutex 作為函數(shù)或方法的參數(shù)傳遞、在獲取 Mutex 的鎖時(shí)阻塞太久、重復(fù)釋放 Mutex 的鎖、在鎖內(nèi)部執(zhí)行阻塞或耗時(shí)操作等。
除了 Mutex 互斥鎖,Go 語(yǔ)言還提供了其他類(lèi)型的鎖,如讀寫(xiě)鎖(sync.RWMutex)、條件變量(sync.Cond)等,可以根據(jù)不同的場(chǎng)景選擇不同類(lèi)型的鎖。
最后,希望你能夠通過(guò)本文對(duì) Mutex 互斥鎖有一個(gè)更深的理解。
到此這篇關(guān)于一文掌握Go語(yǔ)言并發(fā)編程必備的Mutex互斥鎖的文章就介紹到這了,更多相關(guān)Go語(yǔ)言 Mutex互斥鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang的time包:秒、毫秒、納秒時(shí)間戳輸出方式
這篇文章主要介紹了golang的time包:秒、毫秒、納秒時(shí)間戳輸出方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12Go map底層實(shí)現(xiàn)與擴(kuò)容規(guī)則和特性分類(lèi)詳細(xì)講解
這篇文章主要介紹了Go map底層實(shí)現(xiàn)與擴(kuò)容規(guī)則和特性,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-03-03golang中判斷請(qǐng)求是http還是https獲取當(dāng)前訪問(wèn)地址
這篇文章主要為大家介紹了golang中判斷請(qǐng)求是http還是https獲取當(dāng)前訪問(wèn)地址示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10golang?pprof?監(jiān)控goroutine?thread統(tǒng)計(jì)原理詳解
這篇文章主要為大家介紹了golang?pprof?監(jiān)控goroutine?thread統(tǒng)計(jì)原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04go編程中g(shù)o-sql-driver的離奇bug解決記錄分析
這篇文章主要為大家介紹了go編程中g(shù)o-sql-driver的離奇bug解決記錄分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05Golang通過(guò)包長(zhǎng)協(xié)議處理TCP粘包的問(wèn)題解決
本文主要介紹了Golang通過(guò)包長(zhǎng)協(xié)議處理TCP粘包的問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06