Go 的三種擴展原語之 — ErrGroup

概述

除標準庫中提供的同步原語外,Go語言還在子倉庫sync中提供了 4 種擴展原語:

其中 golang/sync/syncmap.MapGo1.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 返回,該方法的不同返回結果有不同的含義:

結構體

golang/sync/errgroup.Group 結構體有 3 個比較重要的部分組成

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 方法,這個方法的執行過程如下:

  1. 調用 sync.WaitGroup.Add 增加待處理的任務

  2. 創建新的 Goroutine 並運行子任務

  3. 返回錯誤時及時調用 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,它只是封裝了基本同步語義以提供更加複雜的功能。我們在使用它時需要注意兩個問題:

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/0Vc2ydy1OtLDRaqYt4jqYg