Go sync包的WaitGroup【同步等待組】詳解

LiberHome發表於2022-05-15

    sync全稱是synchronization,意思是同步。
WaitGroup同步等待組是有一組goroutine要等待執行,而當前的goroutine需要等待這一組goroutine都執行完才能執行。
    下面是WaitGroup的定義:

type WaitGroup struct {
    //
}

    每個WaitGroup中都有一個計數器counter記錄要等待執行gorouting的數量,可以通過Add這個方法來設定同步等待組中等待goroutine的個數。每個goroutine都會執行,執行完之後都會呼叫done方法表示把計數器的值-1,與此同時,使用wait方法阻塞,直到所goroutine都執行完畢。
func (wg *WaitGroup) Add(delta int)

  • 如果計數器的值為0,那麼等待過程中被阻塞的goroutine都會被釋放
  • 如果計數器得知為負,那麼就會引發panic

那麼具體什麼情況下用,怎麼使用呢?
下面舉個例子:

package main

import "fmt"

func main() {
    //waitGroup
    go fun1()
    go fun2()
}

func fun1() {
    for i := 1; i < 10; i++ {
        fmt.Println("print A in fun1", i)
    }
}

func fun2() {
    for i := 0; i < 10; i++ {
        fmt.Println("\t fun2 print: ", i)
    }
}

這樣最後什麼都沒有列印,因為主goroutine在fun1 fun2這兩個goroutine執行之前結束了。這時候就可以用WaitGroup了。

package main

import (
    "fmt"
    "sync"
)

//建立同步等待組
var wg sync.WaitGroup

func main() {
    //waitGroup
    wg.Add(2) //設定計數器為2(goroutine的個數)
    go fun1()
    go fun2()
    fmt.Println("main 進入阻塞狀態, 需要等待wg的goroutine結束")
    wg.Wait() //表示main goroutine進入阻塞
    fmt.Println("計數器歸零 停止阻塞")
}

func fun1() {
    defer wg.Done() //計數器-1
    for i := 1; i < 10; i++ {
        fmt.Println("print A in fun1", i)
    }
}

func fun2() {
    defer wg.Done() //計數器-1
    for i := 0; i < 10; i++ {
        fmt.Println("\t fun2 print: ", i)
    }
}

結果如下:

main 進入阻塞狀態, 需要等待wg的goroutine結束
print A in fun1 1
print A in fun1 2
         fun2 print:  0
         fun2 print:  1
         fun2 print:  2
         fun2 print:  3
         fun2 print:  4
print A in fun1 3
print A in fun1 4
print A in fun1 5
print A in fun1 6
print A in fun1 7
print A in fun1 8
print A in fun1 9
         fun2 print:  5
         fun2 print:  6
         fun2 print:  7
         fun2 print:  8
         fun2 print:  9
計數器歸零 停止阻塞

結果第一行,說明兩個goroutine已經都啟動了,然後交替使用CPU的時間片。
另外 如果函式執行完畢的時候,計數器的值仍然大於0,會引起deadlock。


另外 Go語言並不推薦上述寫法,更加推薦使用chanel。

資料參考:bilibili

相關文章