解決golang sync.Wait()不執行的問題
goroutine 似乎不用解釋太多,可以利用它實現多線程,也可以利用它來實現異步事件。
在使用關鍵字go的過程中,常常會將用到sync.WaitGroup,如下一段代碼。
package main import ( "fmt" "sync" "time" ) func Run() { var wg = &sync.WaitGroup{} go func() { wg.Add(1) fmt.Println("halo world start") time.Sleep(time.Second * 5) fmt.Println("halo world end") wg.Done() }() // time.Sleep(time.Millisecond * 5) // fmt.Println("server will start") wg.Wait() } func main() { Run() } // output: //
期待的結果是打印 halo world start,5秒後打印halo world end,但是結果就是什麼都沒有,並且進程立即就結束瞭。
原因
關鍵字go是異步的,當執行到go,不會立即執行go 後面的內容,而且繼續往下執行。此時wg.Add(1)還沒有來得及執行,wg.Wait()就已經執行,即不會發生等待,進程就結束瞭。
怎麼解決:
隻需要在wg.Wait()前有其他操作,給與足夠的時間讓wg.Add(1)執行即可,
方法一、時間等待,在wg.Wait()前加一句time.Sleep(time.Millisecond*5),既不影響性能,也能讓wg.Add(1)來得及執行
方法二、有IO操作,在wg.Wait()有其他IO操作,比如fmt.Println(“server will start”),原因是std的輸出會將進程從用戶態轉向內核態,打印命令發出後,又切回用戶態,這個狀態的轉換是很有消耗的,wg.Add(1)也就有時間執行。
Don’t worry
是否有存在擔心,方法一的時間等待,等待的時候不夠長,還是讓wg.Add(1)來不及執行。don’t worry.
這裡涉及到goroutine的調度問題,go進程在執行過程中,必須從goroutine隊列中取出一個來執行,當wg.Wait()執行前就算執行time.Sleep(time.Nanosecond), 一納秒,一…一…一納秒,wg.Add(1)也來得及執行,因為主goroutine會被切換到睡眠狀態,go進程必須要取一個線程來執行,就會取到wg.Add(1)這個線程,接下來就順理成章瞭。
同時方法二也是異曲同工,當發出打印的事件,整個進程都會被切換到就緒態,然後再被cpu執行。
補充:【golang】sync.WaitGroup{}的wait()調用位置不同導致意想不到錯誤
協程go多瞭,總覺的天下我有,沒事就喜歡go一個協程,信手拈來,在項目中寫個如下類似代碼:
wh := sync.WaitGroup{} out := make(chan string) go func() { wh.Wait() close(out) }() go func() { for i := 0; i < 2; i++ { wh.Add(1) go tt(out) wh.Done() } }()
想著開個協程去wait所有協程組,測試一下通瞭,沒問題,好牛逼,協程呀!!
可多測試即便就會出現:
send close channel
或者協程定死在某一個,還自已為是的認為自己寫的子方法估計不小心關閉瞭channel,找瞭半天隻找到在wg.wait()後進行瞭關閉。就這樣扣瞭好久,還沒想到自己畫蛇添足的錯誤,經大佬一指點,原來開一個協程,還沒等後一個協程進行wg.add(1)操作,wg.wait()就已經過瞭,關閉瞭channel。
隻好老老實實寫:
wh := sync.WaitGroup{} out := make(chan string) go func() { for i := 0; i < 2; i++ { wh.Add(1) go tt(out) wh.Done() } wh.Wait() close(out) }()
其實就是一個小小的同步問題,旁觀者清呀!!!
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。如有錯誤或未考慮完全的地方,望不吝賜教。
推薦閱讀:
- Go語言如何輕松編寫高效可靠的並發程序
- golang 並發編程之生產者消費者詳解
- 一文帶你瞭解Golang中的WaitGroups
- 在golang中使用Sync.WaitGroup解決等待的問題
- golang coroutine 的等待與死鎖用法