avatar

目录
errgroup的故事

其实这种”XX的故事”源自很久之前和男票之间的一种技术分享游戏,就是把一些小的知识点,以讲事的形式讲给对方听。既提高自己的技术总结能力,相互之间也促进技术分享。其实不在乎多经典多有营养的知识点,
这是为了对某个知识点的巩固,具体内容详解,后期自己再研究即可,抛砖引玉。后来因为和男票处于不同的技术领域,这种游戏停滞了很久。前几天忽然想继续,便有了今天这篇”errgroup的故事”。

一般在go中进行进行并发业务,直接使用go关键字来开启 goroutine即可。但是直接使用go的话函数是无法对返回数据进行处理error的。
这时便可以使用x/sync中的errgroup。它在sync.WaitGroup功能的基础上,增加了错误传递,以及在发生不可恢复的错误时取消整个goroutine集合。

errgroup 包中的 Group 结构体同时由三个比较重要的部分组成:

  • 创建 Context 时返回的 cancel 函数,主要用于通知使用 context 的 Goroutine,由于某些子任务出错,可以停止工作让出资源了
  • 用于等待一组 Goroutine 完成子任务的 WaitGroup
  • 用于接受子任务返回错误err的errOnce

(故事性互动)

errgroup使用起来功能明确,实现思路简洁。你有没有看过errgroup的源码呀?

(我,摇头…) 这种时候不摇头不说没看过,话题怎么继续…

那如果多个子进程中有多个报错,那返回的报错信息是哪一个呢?
看一下源码我们可以很清楚的回答这个问题。

plaintext
1
2
3
4
5
6
7
8
9
10
11
12
// A Group is a collection of goroutines working on subtasks that are part of
// the same overall task.
//
// A zero Group is valid and does not cancel on error.
type Group struct {
cancel func()

wg sync.WaitGroup

errOnce sync.Once
err error
}

源码中errOnce使用了Once,保证了保证某个动作只被执行一次功能。那上面问题的答案,就是…
返回的err,是子Goroutine中的第一个报错信息。

其实当天的故事到这里就结束了,但源码哪有看一半的道理,后面的方法自己继续看一下…

plaintext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// WithContext returns a new Group and an associated Context derived from ctx.
//
// The derived Context is canceled the first time a function passed to Go
// returns a non-nil error or the first time Wait returns, whichever occurs
// first.
func WithContext(ctx context.Context) (*Group, context.Context) {
ctx, cancel := context.WithCancel(ctx)
return &Group{cancel: cancel}, ctx
}

// Wait blocks until all function calls from the Go method have returned, then
// returns the first non-nil error (if any) from them.
func (g *Group) Wait() error {
g.wg.Wait()
if g.cancel != nil {
g.cancel()
}
return g.err
}

// Go calls the given function in a new goroutine.
//
// The first call to return a non-nil error cancels the group; its error will be
// returned by Wait.
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()
}
})
}
}()
}
文章作者: Viola Tangxl
文章链接: https://violatangxl.github.io/2020/01/12/go-errgroup/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 椰子是只猫
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论