Go - 使用 sync.WaitGroup 來實現併發操作

新亮發表於2021-11-06

前言

如果你有一個任務可以分解成多個子任務進行處理,同時每個子任務沒有先後執行順序的限制,等到全部子任務執行完畢後,再進行下一步處理。這時每個子任務的執行可以併發處理,這種情景下適合使用 sync.WaitGroup

雖然 sync.WaitGroup 使用起來比較簡單,但是一不留神很有可能踩到坑裡。

sync.WaitGroup 正確使用

比如,有一個任務需要執行 3 個子任務,那麼可以這樣寫:

func main() {
    var wg sync.WaitGroup

    wg.Add(3)

    go handlerTask1(&wg)
    go handlerTask2(&wg)
    go handlerTask3(&wg)

    wg.Wait()

    fmt.Println("全部任務執行完畢.")
}

func handlerTask1(wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println("執行任務 1")
}

func handlerTask2(wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println("執行任務 2")
}

func handlerTask3(wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println("執行任務 3")
}

執行輸出:

執行任務 3
執行任務 1
執行任務 2
全部任務執行完畢.

sync.WaitGroup 閉坑指南

01

// 正確
go handlerTask1(&wg)

// 錯誤
go handlerTask1(wg)

執行子任務時,使用的 sync.WaitGroup 一定要是 wg 的引用型別!

02

注意不要將 wg.Add() 放在 go handlerTask1(&wg) 中!

例如:

// 錯誤
var wg sync.WaitGroup

go handlerTask1(&wg)

wg.Wait()

...

func handlerTask1(wg *sync.WaitGroup) {
    wg.Add(1)
    defer wg.Done()
    fmt.Println("執行任務 1")
}

注意 wg.Add() 一定要在 wg.Wait() 執行前執行!

03

注意 wg.Add()wg.Done() 的計數器保持一致!其實 wg.Done() 就是執行的 wg.Add(-1)

小結

sync.WaitGroup 使用起來比較簡單,一定要注意不要踩到坑裡。

其實 sync.WaitGroup 使用場景比較侷限,僅適用於等待全部子任務執行完畢後,再進行下一步處理,如果需求是當第一個子任務執行失敗時,通知其他子任務停止執行,這時 sync.WaitGroup 是無法滿足的,需要使用到通知機制(channel)。

以上,希望對你能夠有所幫助。

推薦閱讀

相關文章