Hi,大家好,我是明哥。
在自己學習 Golang 的這段時間里,我寫了詳細的學習筆記放在我的個人微信公眾號 《Go編程時光》,對於 Go 語言,我也算是個初學者,因此寫的東西應該會比較適合剛接觸的同學,如果你也是剛學習 Go 語言,不防關注一下,一起學習,一起成長。
我的在線博客:http://golang.iswbm.com
我的 Github:github.com/iswbm/GolangCodingTime
在前兩篇文章里,我們學習了 協程
和 信道
的內容,里面有很多例子,當時為了保證 main goroutine 在所有的 goroutine 都執行完畢后再退出,我使用了 time.Sleep 這種簡單的方式。
由於寫的 demo 都是比較簡單的, sleep 個 1 秒,我們主觀上認為是夠用的。
但在實際開發中,開發人員是無法預知,所有的 goroutine 需要多長的時間才能執行完畢,sleep 多了吧主程序就阻塞了, sleep 少了吧有的子協程的任務就沒法完成。
因此,使用time.Sleep 是一種極不推薦的方式,今天主要就要來介紹 一下如何優雅的處理這種情況。
1. 使用信道來標記完成
“不要通過共享內存來通信,要通過通信來共享內存”
學習了信道后,我們知道,信道可以實現多個協程間的通信,那么我們只要定義一個信道,在任務完成后,往信道中寫入true,然后在主協程中獲取到true,就認為子協程已經執行完畢。
import "fmt"
func main() {
done := make(chan bool)
go func() {
for i := 0; i < 5; i++ {
fmt.Println(i)
}
done <- true
}()
<-done
}
輸出如下
0
1
2
3
4
2. 使用 WaitGroup
上面使用信道的方法,在單個協程或者協程數少的時候,並不會有什么問題,但在協程數多的時候,代碼就會顯得非常復雜,有興趣可以自己嘗試一下。
那么有沒有一種更加優雅的方式呢?
有,這就要說到 sync包 提供的 WaitGroup 類型。
WaitGroup 你只要實例化了就能使用
var 實例名 sync.WaitGroup
實例化完成后,就可以使用它的幾個方法:
- Add:初始值為0,你傳入的值會往計數器上加,這里直接傳入你子協程的數量
- Done:當某個子協程完成后,可調用此方法,會從計數器上減一,通常可以使用 defer 來調用。
- Wait:阻塞當前協程,直到實例里的計數器歸零。
舉一個例子:
import (
"fmt"
"sync"
)
func worker(x int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 5; i++ {
fmt.Printf("worker %d: %d\n", x, i)
}
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
go worker(1, &wg)
go worker(2, &wg)
wg.Wait()
}
輸出如下
worker 2: 0
worker 2: 1
worker 2: 2
worker 2: 3
worker 2: 4
worker 1: 0
worker 1: 1
worker 1: 2
worker 1: 3
worker 1: 4
以上就是我們在 Go 語言中實現一主多子的協程協作方式,推薦使用 sync.WaitGroup。。
系列導讀
24. 超詳細解讀 Go Modules 前世今生及入門使用