Go 的三種擴展原語之 — ErrGroup
概述
除標準庫中提供的同步原語外,Go
語言還在子倉庫sync
中提供了 4 種擴展原語:
-
golang/sync/errgroup.Group
-
golang/sync/semaphore.Weighted
-
golang/sync/singleflight.Group
-
golang/sync/syncmap.Map
其中 golang/sync/syncmap.Map
在 Go1.9
中移植到了標準庫中。
接下來介紹Go
語言在擴展包中提供的 3 種同步原語 —— golang/sync/errgroup.Group
ErrGroup
golang/sync/errgroup.Group
爲我們在一組 Goroutine
中提供了同步、錯誤傳播以及上下文取消功能,我們可以使用這種方式並行獲取網頁數據:
var g errgroup.Group
var urls = []string{
"http://www.golang.org",
"http://www.google.com"
}
for i := range urls {
url := urls[i]
g.Go(func() error {
resp, err := http.Get(url)
if err == nil {
resp.Body.Close()
}
return err
})
}
if err := g.Wait(); err == nil {
fmt.Println("Successfully fetched all URLs.")
}
golang/sync/errgroup.Group.Go
方法能夠創建一個 Goroutine
並在其中執行傳入的函數,而 golang/sync/errgroup.Group.Wait
會等待所有 Goroutine
返回,該方法的不同返回結果有不同的含義:
-
如果返回錯誤 —— 這一組
Goroutine
最少返回一個錯誤 -
如果返回空值 —— 所有
Goroutine
都成功執行
結構體
golang/sync/errgroup.Group
結構體有 3
個比較重要的部分組成
-
cancel
—— 創建context.Context
時返回的取消函數,用於在多個Goroutine
之間同步取消信號 -
wg
—— 用於等待一組Goroutine
完成子任務的同步原語 -
errOnce
—— 用於保證只接收一個子任務
返回的錯誤
type Group struct {
cancel func()
wg sync.WaitGroup
errOnce sync.Once
err error
}
這些字段共同組成了 golang/sync/errgroup.Group
結構體併爲我提供同步、錯誤傳播以及上下文取消等功能。
接口
我們能通過 golang/sync/errgroup.WithContext
構造器創新的 golang/sync/errgroup.Group
結構體:
func WithContext(ctx context.Context) (*Group, context.Context) {
ctx, cancel := context.WithCancel(ctx)
return &Group{cancel: cancel}, ctx
}
運行新的並行子任務需要使用 golang/sync/errgroup.Group.Go
方法,這個方法的執行過程如下:
-
調用
sync.WaitGroup.Add
增加待處理的任務 -
創建新的
Goroutine
並運行子任務 -
返回錯誤時及時調用
cancel
並對err
賦值,只有最早返回的錯誤纔會被上游感知到,後續錯誤都會被捨棄。
func (g *Group) Go(){
g.wg.Add(1)
go func() {
defer g.wg.Done()
if err := f(); err := nil {
g.errOnce.Do(func () {
g.err = err
if g.cancel != nil {
g.cancel()
}
})
}
}()
}
func (g *Group) Wait() error {
g.wg.Wait()
if g.cancel != nil {
g.cancel()
}
return g.err
}
另一個用於等待的 golang/sync/errgroup.Group.Wait
方法只是調用了 sync.WaitGroup.Wait
,在子任務全部完成時取消 context.Context 並返回可能出現的錯誤。
小結
golang/sync/errgroup.Group
的實現沒有涉及底層和運行包中的API
,它只是封裝了基本同步語義以提供更加複雜的功能。我們在使用它時需要注意兩個問題:
-
golang/sync/errgroup.Group
在出現錯誤或者等待結束後,會調用context.Context
的cancel
方法同步取消信號 -
只有第一個出現的錯誤纔會被返回,剩餘錯誤會被直接丟棄
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/0Vc2ydy1OtLDRaqYt4jqYg