Go語言除了可以使用通道(channel)和互斥鎖進行兩個併發程序間的同步外,還可以使用等待組進行多個任務的同步,等待組可以保證在併發環境中完成指定數量的任務。
在sync.WaitGroup(等待組)類型中,每個sync.WaitGroup值在內部維護着一個計數,此計數的初始默認值爲零。
等待組有下面幾個方法可以用,如下表示:
對於一個可尋址的sync.WaitGroup值wg:
1、我們可以使用方法調用wg.Add(delta)來改變值 wg 維護的計數。
2、方法調用 wg.Done() 和 wg.Add(-1) 是完全等價的。
3、如果一個 wg.Add(delta) 或者 wg.Done() 調用將 wg 維護的計數更改成一個負數,一個恐慌將產生。
- 當一個協程調用了 wg.Wait() 時,
- 如果此時 wg 維護的計數爲零,則此 wg.Wait() 此操作爲一個空操作(noop);
- 否則(計數爲一個正整數),此協程將進入阻塞狀態。當以後其它某個協程將此計數更改至 0 時(一般通過調用 wg.Done()),此協程將重新進入運行狀態(即 wg.Wait() 將返回)。
等待組內部擁有一個計數器,計數器的值可以通過方法調用實現計數器的增加和減少。當我們添加了N個併發任務進行工作時,就將等待組的計數器值增加N。每個任務完成時,這個值減1.同時,在另一個goroutine中等待這個等待組的計數值爲0時,表示所有任務已經完成。
package main
import (
"fmt"
"net/http"
"sync"
)
func main() {
//聲明一個等待組
var wg sync.WaitGroup
//準備一些列的網址地址
var urls = []string{
}
//遍歷這些地址
for _, url := range urls {
//每一個任務開始時,將等待組增加1
wg.Add(1)
go func(url string) {
//使用defer,表示函數完成時將等待數組值減1
defer wg.Done()
//使用http訪問提供的地址
_, err := http.Get(url)
//訪問完成後,打印地址和可能發生的錯誤
fmt.Println(url, err)
//通過參數傳遞url地址
}(url)
}
//等待所有的任務完成
wg.Wait()
fmt.Println("over")
}
輸出:
http://www.github.com/ Get http://www.github.com/: read tcp 192.168.2.129:55170->13.229.188.59:80: wsarecv: An existing connection was forcibly closed by the remote host.
https://www.qiniu.com/ <nil>
https://www.golangtc.com/ <nil>
over
代碼說明如下:
第 12 行,聲明一個等待組,對一組等待任務只需要一個等待組,而不需要每一個任務都使用一個等待組。
第 15 行,準備一系列可訪問的網站地址的字符串切片。
第 22 行,遍歷這些字符串切片。
第 25 行,將等待組的計數器加1,也就是每一個任務加 1。
第 28 行,將一個匿名函數開啓併發。
第 31 行,在匿名函數結束時會執行這一句以表示任務完成。wg.Done() 方法等效於執行 wg.Add(-1)。
第 34 行,使用 http 包提供的 Get() 函數對 url 進行訪問,Get() 函數會一直阻塞直到網站響應或者超時。
第 37 行,在網站響應和超時後,打印這個網站的地址和可能發生的錯誤。
第 40 行,這裏將 url 通過 goroutine 的參數進行傳遞,是爲了避免 url 變量通過閉包放入匿名函數後又被修改的問題。
第 44 行,等待所有的網站都響應或者超時後,任務完成,Wait 就會停止阻塞。