我们知道在 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。