(轉)Go sync.WaitGroup的用法
阿新 • • 發佈:2021-06-20
介紹
經常會看到以下了程式碼:
package main import ( "fmt" "time" ) func main(){ for i := 0; i < 100 ; i++{ go fmt.Println(i) } time.Sleep(time.Second) }
主執行緒為了等待goroutine都執行完畢,不得不在程式的末尾使用time.Sleep()
來睡眠一段時間,等待其他執行緒充分執行。對於簡單的程式碼,100個for迴圈可以在1秒之內執行完畢,time.Sleep()
也可以達到想要的效果。
但是對於實際生活的大多數場景來說,1秒是不夠的,並且大部分時候我們都無法預知for迴圈內程式碼執行時間的長短。這時候就不能使用time.Sleep()
可以考慮使用管道來完成上述操作:
func main() { c := make(chan bool, 100) for i := 0; i < 100; i++ { go func(i int) { fmt.Println(i) c <- true }(i) } for i := 0; i < 100; i++ { <-c } }
首先可以肯定的是使用管道是能達到我們的目的的,而且不但能達到目的,還能十分完美的達到目的。
但是管道在這裡顯得有些大材小用,因為它被設計出來不僅僅只是在這裡用作簡單的同步處理,在這裡使用管道實際上是不合適的。而且假設我們有一萬、十萬甚至更多的for
對於這種情況,go語言中有一個其他的工具sync.WaitGroup
能更加方便的幫助我們達到這個目的。
WaitGroup
物件內部有一個計數器,最初從0開始,它有三個方法:Add(), Done(), Wait()
用來控制計數器的數量。Add(n)
把計數器設定為n
,Done()
每次把計數器-1
,wait()
會阻塞程式碼的執行,直到計數器地值減為0
。
使用WaitGroup
將上述程式碼可以修改為:
func main() { wg := sync.WaitGroup{} wg.Add(100) for i := 0; i < 100; i++ { go func(i int) { fmt.Println(i) wg.Done() }(i) } wg.Wait() }
這裡首先把wg 計數設定為100, 每個for迴圈執行完畢都把計數器減一,主函式中使用Wait() 一直阻塞,直到wg為零——也就是所有的100個for迴圈都執行完畢。相對於使用管道來說,WaitGroup 輕巧了許多。
注意事項
1. 計數器不能為負值
我們不能使用Add()
給wg
設定一個負值,否則程式碼將會報錯:
panic: sync: negative WaitGroup counter goroutine 1 [running]: sync.(*WaitGroup).Add(0xc042008230, 0xffffffffffffff9c) D:/Go/src/sync/waitgroup.go:75 +0x1d0 main.main() D:/code/go/src/test-src/2-Package/sync/waitgroup/main.go:10 +0x54
同樣使用Done()
也要特別注意不要把計數器設定成負數了。
2. WaitGroup物件不是一個引用型別
WaitGroup
物件不是一個引用型別,在通過函式傳值的時候需要使用地址:
func main() { wg := sync.WaitGroup{} wg.Add(100) for i := 0; i < 100; i++ { go f(i, &wg) } wg.Wait() } // 一定要通過指標傳值,不然程序會進入死鎖狀態 func f(i int, wg *sync.WaitGroup) { fmt.Println(i) wg.Done() }