我们知道在 go 语言中很容易开启携程进行并发任务, 但是如何处理并发过程中的错误是非常棘手的,接下来我就来介绍 errgroup 的用法。

errgroup

errgroup 包里主要是一个结构体和相应的方法,它可以让你在并发任务中处理错误。

type Group struct {
    // context 的 cancel 方法
	cancel func()
	// 复用 WaitGroup
	wg sync.WaitGroup

	sem chan token
	
	// 用来保证只会接受一次错误
	errOnce sync.Once
	
	// 保存第一个返回的错误
	err     error
}
func WithContext(ctx context.Context) (*Group, context.Context)
func (g *Group) done()
func (g *Group) Wait() error
func (g *Group) Go(f func() error)
func (g *Group) TryGo(f func() error) bool
func (g *Group) SetLimit(n int) 

通过 WithContext 可以创建一个可以取消的 Group,当然除此之外也可以零值的 Group 也可以直接使用,但是出错之后就不会取消其他的 goroutine 了. Go方法 传入一个函数参数,会启动一个 goroutine 处理。

Wait 类似 WaitGroup 的 Wait 方法,等待所有的 goroutine 结束后退出,返回的错误是一个出错的 err。

TryGo 是和 SetLimit 配套的,只有当 group 中的 goroutines 数量小于配置的数量时,才会在 goroutine 中调用函数。 TryGo 用来判断 goroutine 是否启动。

例子

接下来看一个官方使用 errgroup 的例子。

package main

import (
	"fmt"
	"log"
	"net/http"

	"golang.org/x/sync/errgroup"
)

func main() {
	g := new(errgroup.Group)
	var urls = []string{
		"http://www.golang.org/",
		"http://www.somestupidname.com/",
		"htt://www.yixieqitawangzhi.com",
	}
	for _, url := range urls {
		// Launch a goroutine to fetch the URL.
		url := url // https://golang.org/doc/faq#closures_and_goroutines
		g.Go(func() error {
			// Fetch the URL.
			resp, err := http.Get(url)
			if err == nil {
				log.Printf("%s: %s", url, resp.Status)
				resp.Body.Close()
			}
			return err
		})
	}
	// Wait for all HTTP fetches to complete.
	if err := g.Wait(); err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println("Successfully fetched all URLs.")

}

这个例子很简单,就是当我们并发处理某个任务时,errgroup 会保存发生的第一个 error,当所有的 goroutine 都结束后,会返回这个 error。

参考