Go?WaitGroup及Cond底層實(shí)現(xiàn)原理
WaitGroup
概念
Go
標(biāo)準(zhǔn)庫(kù)提供了WaitGroup
原語(yǔ), 可以用它來(lái)等待一批 Goroutine 結(jié)束
底層數(shù)據(jù)結(jié)構(gòu)
// A WaitGroup must not be copied after first use. type WaitGroup struct { noCopy noCopy state1 [3]uint32 }
其中 noCopy
是 golang 源碼中檢測(cè)禁止拷貝的技術(shù)。如果程序中有 WaitGroup 的賦值行為,使用 go vet
檢查程序時(shí),就會(huì)發(fā)現(xiàn)有報(bào)錯(cuò)。但需要注意的是,noCopy 不會(huì)影響程序正常的編譯和運(yùn)行。
state1
主要是存儲(chǔ)著狀態(tài)和信號(hào)量,狀態(tài)維護(hù)了 2 個(gè)計(jì)數(shù)器,一個(gè)是請(qǐng)求計(jì)數(shù)器counter ,另外一個(gè)是等待計(jì)數(shù)器waiter(已調(diào)用 WaitGroup.Wait
的 goroutine 的個(gè)數(shù))
當(dāng)數(shù)組的首地址是處于一個(gè)8
字節(jié)對(duì)齊的位置上時(shí),那么就將這個(gè)數(shù)組的前8
個(gè)字節(jié)作為64
位值使用表示狀態(tài),后4
個(gè)字節(jié)作為32
位值表示信號(hào)量(semaphore
);同理如果首地址沒(méi)有處于8
字節(jié)對(duì)齊的位置上時(shí),那么就將前4
個(gè)字節(jié)作為semaphore
,后8
個(gè)字節(jié)作為64
位數(shù)值。
使用方法
在WaitGroup里主要有3個(gè)方法:
WaitGroup.Add()
:可以添加或減少請(qǐng)求的goroutine數(shù)量,Add(n)
將會(huì)導(dǎo)致 counter += n
WaitGroup.Done()
:相當(dāng)于Add(-1),Done()
將導(dǎo)致 counter -=1
,請(qǐng)求計(jì)數(shù)器counter為0 時(shí)通過(guò)信號(hào)量調(diào)用runtime_Semrelease
喚醒waiter線程
WaitGroup.Wait()
:會(huì)將 waiter++
,同時(shí)通過(guò)信號(hào)量調(diào)用 runtime_Semacquire(semap)
阻塞當(dāng)前 goroutine
func main() { var wg sync.WaitGroup for i := 1; i <= 5; i++ { wg.Add(1) go func() { defer wg.Done() println("hello") }() } wg.Wait() }
Cond
概念
Go
標(biāo)準(zhǔn)庫(kù)提供了Cond
原語(yǔ),可以讓 Goroutine 在滿足特定條件時(shí)被阻塞和喚醒
底層數(shù)據(jù)結(jié)構(gòu)
type Cond struct { noCopy noCopy // L is held while observing or changing the condition L Locker notify notifyList checker copyChecker } type notifyList struct { wait uint32 notify uint32 lock uintptr // key field of the mutex head unsafe.Pointer tail unsafe.Pointer }
主要有4
個(gè)字段:
nocopy
: golang 源碼中檢測(cè)禁止拷貝的技術(shù)。如果程序中有 WaitGroup 的賦值行為,使用 go vet
檢查程序時(shí),就會(huì)發(fā)現(xiàn)有報(bào)錯(cuò),但需要注意的是,noCopy 不會(huì)影響程序正常的編譯和運(yùn)行
checker
:用于禁止運(yùn)行期間發(fā)生拷貝,雙重檢查(Double check
)
L
:可以傳入一個(gè)讀寫鎖或互斥鎖,當(dāng)修改條件或者調(diào)用Wait
方法時(shí)需要加鎖
notify
:通知鏈表,調(diào)用Wait()
方法的Goroutine
會(huì)放到這個(gè)鏈表中,從這里獲取需被喚醒的Goroutine列表
使用方法
在Cond里主要有3個(gè)方法:
sync.NewCond(l Locker)
: 新建一個(gè) sync.Cond 變量,注意該函數(shù)需要一個(gè) Locker 作為必填參數(shù),這是因?yàn)樵?nbsp;cond.Wait()
中底層會(huì)涉及到 Locker 的鎖操作Cond.Wait()
: 阻塞等待被喚醒,調(diào)用Wait函數(shù)前需要先加鎖;并且由于Wait函數(shù)被喚醒時(shí)存在虛假喚醒等情況,導(dǎo)致喚醒后發(fā)現(xiàn),條件依舊不成立,因此需要使用 for 語(yǔ)句來(lái)循環(huán)地進(jìn)行等待,直到條件成立為止Cond.Signal()
: 只喚醒一個(gè)最先 Wait 的 goroutine,可以不用加鎖Cond.Broadcast()
: 喚醒所有Wait的goroutine,可以不用加鎖
package main import ( "fmt" "sync" "sync/atomic" "time" ) var status int64 func main() { c := sync.NewCond(&sync.Mutex{}) for i := 0; i < 10; i++ { go listen(c) } go broadcast(c) time.Sleep(1 * time.Second) } func broadcast(c *sync.Cond) { // 原子操作 atomic.StoreInt64(&status, 1) c.Broadcast() } func listen(c *sync.Cond) { c.L.Lock() for atomic.LoadInt64(&status) != 1 { c.Wait() // Wait 內(nèi)部會(huì)先調(diào)用 c.L.Unlock(),來(lái)先釋放鎖,如果調(diào)用方不先加鎖的話,會(huì)報(bào)錯(cuò) } fmt.Println("listen") c.L.Unlock() }
以上就是Go WaitGroup及Cond底層實(shí)現(xiàn)原理的詳細(xì)內(nèi)容,更多關(guān)于Go WaitGroup Cond原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Beego中ORM操作各類數(shù)據(jù)庫(kù)連接方式詳細(xì)示例
這篇文章主要為大家介紹了Beego中ORM操作各類數(shù)據(jù)庫(kù)連接方式詳細(xì)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04一文詳解GO如何實(shí)現(xiàn)Redis的AOF持久化
這篇文章主要為大家詳細(xì)介紹了GO如何實(shí)現(xiàn)Redis的AOF持久化的,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解一下2023-03-03Golang實(shí)現(xiàn)Redis事務(wù)深入探究
這篇文章主要介紹了Golang實(shí)現(xiàn)Redis事務(wù)深入探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01使用Lumberjack+zap進(jìn)行日志切割歸檔操作
這篇文章主要介紹了使用Lumberjack+zap進(jìn)行日志切割歸檔操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12解決Golang小數(shù)float64在實(shí)際工程中加減乘除的精度問(wèn)題
這篇文章主要介紹了解決Golang小數(shù)float64在實(shí)際工程中加減乘除的精度問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-03-03深入講解Go語(yǔ)言中函數(shù)new與make的使用和區(qū)別
大家都知道Go語(yǔ)言中的函數(shù)new與函數(shù)make一直是新手比較容易混淆的東西,看著相似,但其實(shí)不同,不過(guò)解釋兩者之間的不同也非常容易,下面這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言中函數(shù)new與make區(qū)別的相關(guān)資料,需要的朋友可以參考下。2017-10-10golang定時(shí)任務(wù)cron項(xiàng)目實(shí)操指南
Go實(shí)現(xiàn)的cron 表達(dá)式的基本語(yǔ)法跟linux 中的 crontab基本是類似的,下面這篇文章主要給大家介紹了關(guān)于golang定時(shí)任務(wù)cron項(xiàng)目實(shí)操的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12