golang基礎之waitgroup用法以及使用要點

一、前言

waitgroup在golang中,用於線程同步,指等待一個組,等待一個系列執行完成後,才會向下執行,可以解決一個 進程goroutine 等待多個該進程啟動的子線程goroutine 都正常運行完成的場景,這個比較常見的場景就是例如 後端 main processer 啟動瞭多個消費者worker幹活,還有爬蟲並發爬取數據,多線程下載等等,為瞭保證主進程在所有的子線程完成後再退出,這時就要用上waitgroup

二、waitgroup使用示例

我們這裡模擬一個 worker 的例子

package main

import (
    "fmt"
    "sync"
    "time"
)
func worker(idx int, out chan struct{}, wg *sync.WaitGroup) {
	 defer wg.Done()
        time.Sleep(1 * time.Second)
        fmt.Println(time.Now())
        fmt.Println(idx )
        <-out 
}
func main() {
    wg := new(sync.WaitGroup)
    in := make(chan struct{}, 20)
    for i := 0; i < 200; i++ {
        in <- struct{}{}
        wg.Add(1)
        go worker(i, in, wg)
    }
    wg.Wait()
}

在這段代碼中,main最後一行是

wg.Wait()

這行代碼保證有所的200個子線程全部都執行完成後才會退出main函數,如果沒有最後一行wg.Wait(),可能會出現for循環遍歷完程序就直接退出瞭,有可能隻有不確定個幾個子線程執行完成,其它線程由於主程序main退出後就直接退出瞭

從這個例子我們也可以看到 waitgroup通常配合來限制並發線程個數和確保所有的線程都最終都執行完成
這段代碼中 ws 有三個緩沖,所以並發的數量是20,超過20個就要等待執行完成釋放所占用通道後才能再開新的線程

上面的代碼,會在執行到wg.Wait()後等待,直到所有的200線程全部執行完後才會繼續往下執行

可以説這段代碼非常精妙,示例代碼執行結果如下:

同時我們也可以測試一下把最後一行

//wg.Wait()

註釋掉,我們看一下程序會怎麼執行

註釋後,我們看到程序完成時,隻有171個線程完成運行,剩下的20幾個線程異常結束瞭,看不到任何返回結果

三、waitgroup使用註意事項

同時我們在使用waitgroup時也要註意一些坑:

1、 Add一個負數

如果計數器的值小於0會直接panic

2、 Add在Wait之後調用

比如一些子協程開頭調用Add結束調用Wait,這些 Wait無法阻塞子協程。正確做法是在開啟子協程之前先Add特定的值。

3、 未置為0就重用

WaitGroup可以完成一次編排任務,計數值降為0後可以繼續被其他任務所用,但是不要在還沒使用完的時候就用於其他任務,這樣由於帶著計數值,很可能出問題。

4、 復制waitgroup

WaitGroup有nocopy字段,不能被復制。也意味著WaitGroup不能作為函數的參數

四、waitgroup使用總結

WaitGroup是Golang應用開發過程中經常使用的並發控制技術,學習golang是我們必須要掌握和理解的機制之一,建議有時間瞭大傢再進一步的研究一下WaitGroup的底層實現邏輯。

附:陷阱避免

1)WaitGroup 同步的是 goroutine, 如果在 goroutine 中進行 Add(1) 操作,可能在這些 goroutine 還沒來得及 Add(1) 已經執行 Wait 操作,造成程序退出。

2)WaitGroup 傳遞給goroutine的時候,應該采用引用方式,從而避免發生副本拷貝而死鎖。

總結

到此這篇關於golang基礎之waitgroup用法以及使用要點的文章就介紹到這瞭,更多相關golang waitgroup用法內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: