errgroup 在 ```WaitGroup``` 的基础上实现子协程错误传递, 同时使用 context 控制协程的生命周期。
## 使用
errgroup 的使用非常简单
```go
package main
import (
"context"
"fmt"
"time"
"golang.org/x/sync/errgroup"
)
func main() {
group, _ := errgroup.WithContext(context.Background())
for i := 0; i < 5; i++ {
index := i
group.Go(func() error {
fmt.Printf("start to execute the %d gorouting\n", index)
time.Sleep(time.Duration(index) * time.Second)
if index%2 == 0 {
return fmt.Errorf("something has failed on grouting:%d", index)
}
fmt.Printf("gorouting:%d end\n", index)
return nil
})
}
if err := group.Wait(); err != nil {
fmt.Println(err)
}
}
```
### 输出
输出结果如下:
start to execute the 0 gorouting
start to execute the 3 gorouting
start to execute the 2 gorouting
start to execute the 1 gorouting
start to execute the 4 gorouting
gorouting:1 end
gorouting:3 end
something has failed on grouting:0
### 代码分析
不管是否有协程执行失败, wait()都要等待所有协程执行完成
使用方法与 ```WaitGroup``` 类似。只是封装了 ```WaitGroup``` 的``` Add()```和 ```Wait()```方法。
* 首先传递 context 初始化 errgroup 对象
* 每一个 group.Go() 都会新启一个协程, ```Go()```函数接受一个 func() error 函数类型
* 使用 ```Wait()```方法阻塞主协程,直到所有子协程执行完成
## 分析
errGroup 的结构如下:
```go
type Group struct {
cancel func() //context cancel()
wg sync.WaitGroup
errOnce sync.Once //只会传递第一个出现错的协程的 error
err error //传递子协程错误
}
```
### withContext
```go
func WithContext(ctx context.Context) (*Group, context.Context) {
ctx, cancel := context.WithCancel(ctx)
return &Group{cancel: cancel}, ctx
}
```
### Go
```go
func (g *Group) Go(f func() error) {
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()
}
})
}
}()
}
```
### 小结
* errgroup 可以捕获和记录子协程的错误(只能记录最先出错的协程的错误)
* errgroup 可以控制协程并发顺序。确保子协程执行完成后再执行主协程
* errgroup 可以使用 context 实现协程撤销。或者超时撤销。子协程中使用 ctx.Done()来获取撤销信号
### 参考
[errgroup godoc](https://godoc.org/golang.org/x/sync/errgroup)
有疑问加站长微信联系(非本文作者))