waitGroup源碼解讀原文
WaitGroup定義:
state 是 WaitGroup 的核心,它是一個(gè)無(wú)符號(hào)的 64 位整型,并且用的是 atomic 包中的 Uint64,所以 state 本身是線程安全的。至于 atomic.Uint64 為什么能保證線程安全,因?yàn)樗褂昧?CompareAndSwap(CAS) 操作,而這個(gè)操作依賴(lài)于 CPU 提供的原子性指令,是 CPU 級(jí)的原子操作。
state 的高 32 位是計(jì)數(shù)器(counter),低 32 位是等待者數(shù)量(waiters)。其中計(jì)數(shù)器其實(shí)就是 Add(int) 數(shù)量的總和,譬如 Add(1) 后再 Add(2),那么這個(gè)計(jì)數(shù)器就是 1 + 2 = 3;而等待數(shù)量就是現(xiàn)在有多少 goroutine 在執(zhí)行 Wait() 等待 WaitGroup 被釋放。
信號(hào)量(Semaphore)是一種用于實(shí)現(xiàn)多進(jìn)程或多線程之間同步和互斥的機(jī)制
信號(hào)量本質(zhì)上可以簡(jiǎn)單理解為一個(gè)整型數(shù),主要包含兩種操作:P(Proberen,測(cè)試)操作和 V(Verhogen,增加)操作。其中,P 操作會(huì)嘗試獲取一個(gè)信號(hào)量,如果信號(hào)量的值大于 0,則將信號(hào)量的值減 1 并繼續(xù)執(zhí)行;否則,當(dāng)前進(jìn)程或線程就會(huì)被阻塞,直到有其他進(jìn)程或線程釋放這個(gè)信號(hào)量為止。V 操作則是釋放一個(gè)信號(hào)量,將信號(hào)量的值加 1。
可以把信號(hào)量看作是一種類(lèi)似鎖的東西,P 操作相當(dāng)于獲取鎖,而 V 操作相當(dāng)于釋放鎖。由于信號(hào)量是一種操作系統(tǒng)級(jí)別的機(jī)制,通常由內(nèi)核提供支持,因此我們不用擔(dān)心上述對(duì)信號(hào)量的操作本身會(huì)產(chǎn)生競(jìng)態(tài)條件,相信內(nèi)核能搞定這種東西。
-race 標(biāo)志可以啟用數(shù)據(jù)競(jìng)爭(zhēng)檢測(cè)器 (邏輯忽略)
`runtime_Semrelease` 和 `runtime_Semacquire` 是 Go 語(yǔ)言運(yùn)行時(shí)的兩個(gè)函數(shù),用于實(shí)現(xiàn)協(xié)程之間的同步和通信。
`runtime_Semacquire` 函數(shù)實(shí)現(xiàn)了協(xié)程的等待操作。它接受一個(gè) `*uint32` 類(lèi)型的參數(shù) `s`,表示一個(gè)信號(hào)量。當(dāng)協(xié)程調(diào)用該函數(shù)時(shí),如果信號(hào)量的值為 0,則該協(xié)程會(huì)被阻塞,直到其他協(xié)程調(diào)用了 `runtime_Semrelease` 函數(shù)將信號(hào)量的值增加為 1,才能繼續(xù)執(zhí)行。
`runtime_Semrelease` 函數(shù)實(shí)現(xiàn)了協(xié)程的通知操作。它接受一個(gè) `*uint32` 類(lèi)型的參數(shù) `s`,表示一個(gè)信號(hào)量。當(dāng)協(xié)程調(diào)用該函數(shù)時(shí),會(huì)將信號(hào)量的值增加為 1,并喚醒因等待該信號(hào)量而被阻塞的協(xié)程。如果有多個(gè)協(xié)程正在等待該信號(hào)量,只會(huì)喚醒其中一個(gè)協(xié)程。
這兩個(gè)函數(shù)的實(shí)現(xiàn)原理是基于操作系統(tǒng)提供的原子操作和阻塞操作。在 POSIX (可移植操作系統(tǒng))系統(tǒng)中,可以使用 `sem_init`、`sem_wait` 和 `sem_post` 等函數(shù)來(lái)實(shí)現(xiàn)信號(hào)量的操作。在 Windows 系統(tǒng)中,可以使用 `CreateSemaphore`、`WaitForSingleObject` 和 `ReleaseSemaphore` 等函數(shù)來(lái)實(shí)現(xiàn)信號(hào)量的操作。
在 Go 語(yǔ)言中,由于需要支持跨平臺(tái)運(yùn)行,因此使用了一些平臺(tái)無(wú)關(guān)的方法來(lái)實(shí)現(xiàn)信號(hào)量。具體來(lái)說(shuō),Go 語(yǔ)言運(yùn)行時(shí)使用了一個(gè)名為 M:N 調(diào)度器的機(jī)制,其中 M 個(gè)用戶(hù)級(jí)線程(goroutine)被映射到 N 個(gè)操作系統(tǒng)級(jí)線程上執(zhí)行。在 M:N 調(diào)度器中,Go 語(yǔ)言運(yùn)行時(shí)會(huì)維護(hù)一個(gè)全局的等待隊(duì)列和一個(gè)全局的就緒隊(duì)列,來(lái)實(shí)現(xiàn)協(xié)程之間的同步和通信。當(dāng)一個(gè)協(xié)程調(diào)用 `runtime_Semacquire` 函數(shù)時(shí),Go 語(yǔ)言運(yùn)行時(shí)會(huì)將該協(xié)程加入到全局的等待隊(duì)列中,并將其從當(dāng)前線程上移除,然后阻塞該線程。當(dāng)另一個(gè)協(xié)程調(diào)用 `runtime_Semrelease` 函數(shù)時(shí),Go 語(yǔ)言運(yùn)行時(shí)會(huì)將一個(gè)等待該信號(hào)量的協(xié)程從全局的等待隊(duì)列中取出,并將其加入到全局的就緒隊(duì)列中,然后喚醒一個(gè)空閑的線程來(lái)執(zhí)行該協(xié)程。這樣就實(shí)現(xiàn)了協(xié)程之間的同步和通信。