1. 程式人生 > 實用技巧 >Go語言學習之Go協程:WaitGroup

Go語言學習之Go協程:WaitGroup

我們之前學習了協程和通道,裡面有很多例子,當時為了保證main goroutine在所有的goroutine都執行完畢後在退出,我們使用了time.Sleep這種方式

由於寫的demo都是很簡單的,sleep個1秒,我們感覺應該是夠用的

但是在實際開發中,我們無法預知,所有的goroutine需要多長時間才能執行完畢,sleep多了 主程式就會阻塞,sleep少了有的子協程的任務無法完成

我們今天來介紹一下 怎麼優雅的處理這種情況

1.使用通道來標記完成

通道可以實現多個協程間的通訊,那麼我們只需要定義一個通道,在任務完成後,往通道中寫入true,然後主協程中獲取到true,就認為子協程執行完畢

import "fmt"

func main() {
    done := make(chan bool)
    //開一個協程去執行
    go func() {
        for i := 0; i < 5; i++ {
            fmt.Println(i)
        }
        //執行完畢,往信道里寫入true
        done <- true
    }()
    //主協程中如果獲取到信道里時true 就退出
    <-done
}    

輸出如下

0
1
2
3
4

2.使用WaitGroup

上面使用的方法,在單個協程或者協程數量比較少的時候不會有什麼問題,但在協程數多的時候,程式碼就會很複雜

更優雅的方式就是使用WaitGroup

WaitGroup只要例項化了就能用

var 例項名 sync.WaitGroup 

例項化完成後,就可以使用它的幾個方法:

Add:初始值為0,你傳入的值會往計數器上加,這裡直接傳入子協程的數量

Done: 當某個子協程完成後,可以呼叫此方法,就會從計數器上減1,通常可以使用defer來呼叫

Wait: 阻塞當前協程,知道計數器的值歸0

舉一個例子

// Startup the EventRouter
    //這裡有一個協程, 所以計數器的值是1
    wg.Add(1)
//協程 go func() {
//defer的作用是在eventRouter.run函式執行完,在執行defer的語句,所以這裡就是程式執行完了,執行wg.Done()把計數器裡的值減1
defer wg.Done() eventRouter.Run(stop) }() // Startup the Informer(s) glog.Infof("Starting shared Informer(s)") sharedInformers.Start(stop) //主協程一直呼叫wg.Wait()方法阻塞著,直到計數器的值為0在執行後面的程式碼 wg.Wait()
//執行這個程式碼的時候,說明計數器歸0了,wg.Wait失效,不會阻塞主協程了 glog.Warningf(
"Exiting main()") os.Exit(1)

上面使用通道的方法,在單個協程或者協程數少的時候,並不會有什麼問題,但在協程數多的時候,程式碼就會顯得非常複雜