WaitGroup并發控制
sync.WaitGroup內部維護著一個計數器,計數器的值可以增加和減少。
例如當我們啟動了N 個并發任務時,就通過Add()將計數器值增加N。
每個任務完成時通過調用Done()方法將計數器減1。
通過調用Wait()來等待并發任務執行完,當計數器值為0時,表示所有并發任務已經完成。
const N = 10
var wg = &sync.WaitGroup{}
func main() {
for i := 0; i < N; i++ {
//wg.Add(1) 正確寫法
go func(i int) {
wg.Add(1)
println(i)
defer wg.Done()
}(i)
}
wg.Wait()
}
結果
結果不唯一,代碼存在風險, 所有go未必都能執行到
這是使用WaitGroup經常犯下的錯誤!請各位同學多次運行就會發現輸出都會不同甚至又出現報錯的問題。 這是因為go
執行太快了,導致wg.Add(1)
還沒有執行main函數就執行完畢了。
底層原理
type WaitGroup struct {
noCopy noCopy
state1 [3]uint32 // uint32數組一共12個字節,前8個即uint64記錄 高8位記錄需要等待的數量 低8位正在等待 的數量 ,后4個字節存儲信號量,用于喚醒
}
1.核心原理就是通過之前說的64位的uint64來進行計數,采用高位記錄需要Done的數量,低位記錄Wait的數量,然后排隊休眠等待喚醒
2.如果發現當前count>0則 Wait的goroutine會進行排隊
3.任務完成后的goroutine則進行Done操作,直到count==0,則完成,就喚醒所有因為wait操作睡眠的goroutine