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!
推薦閱讀:
- Go語言如何輕松編寫高效可靠的並發程序
- 在golang中使用Sync.WaitGroup解決等待的問題
- Go並發:使用sync.WaitGroup實現協程同步方式
- 示例剖析golang中的CSP並發模型
- 一文帶你瞭解Golang中的WaitGroups