errgroup

前言

来看下errgroup的实现

如何使用

func main() {
var eg errgroup.Group eg.Go(func() error {
return errors.New("test1")
}) eg.Go(func() error {
return errors.New("test2")
}) if err := eg.Wait(); err != nil {
fmt.Println(err)
}
}

类比于waitgroup,errgroup增加了一个对goroutine错误收集的作用。

不过需要注意的是:

errgroup返回的第一个出错的goroutine抛出的err

errgroup中还可以加入context

func main() {
eg, ctx := errgroup.WithContext(context.Background()) eg.Go(func() error {
// test1函数还可以在启动很多goroutine
// 子节点都传入ctx,当test1报错,会把test1的子节点一一cancel
return test1(ctx)
}) eg.Go(func() error {
return test1(ctx)
}) if err := eg.Wait(); err != nil {
fmt.Println(err)
}
} func test1(ctx context.Context) error {
return errors.New("test2")
}

实现原理

代码很简单

type Group struct {
// 一个取消的函数,主要来包装context.WithCancel的CancelFunc
cancel func() // 还是借助于WaitGroup实现的
wg sync.WaitGroup // 使用sync.Once实现只输出第一个err
errOnce sync.Once // 记录下错误的信息
err error
}

还是在WaitGroup的基础上实现的

WithContext

// 返回一个被context.WithCancel重新包装的ctx

func WithContext(ctx context.Context) (*Group, context.Context) {
ctx, cancel := context.WithCancel(ctx)
return &Group{cancel: cancel}, ctx
}

里面使用了context,通过context.WithCancel对传入的context进行了包装

WithCancel函数返回的CancelFunc被调用或者是父节点的done channel被关闭(父节点的 CancelFunc 被调用),此 context(子节点)的 done channel 也会被关闭。

errgroup把返回的CancelFunc包进了自己的cancel中,来实现对使用errgroupctx启动的goroutine的取消操作。

Go

// 启动取消阻塞的goroutine
// 记录第一个出错的goroutine的err信息
func (g *Group) Go(f func() error) {
// 借助于waitgroup实现
g.wg.Add(1) go func() {
defer g.wg.Done() // 执行出错
if err := f(); err != nil {
// 通过sync.Once记录下第一个出错的err信息
g.errOnce.Do(func() {
g.err = err
// 如果包装了cancel,也就是context的CancelFunc,执行退出操作
if g.cancel != nil {
g.cancel()
}
})
}
}()
}

1、借助于waitgroup实现对goroutine阻塞;

2、通过sync.Once记录下,第一个出错的goroutine的错误信息;

3、如果包装了contextCancelFunc,在出错的时候进行退出操作。

Wait

// 阻塞所有的通过Go加入的goroutine,然后等待他们一个个执行完成
// 然后返回第一个出错的goroutine的错误信息
func (g *Group) Wait() error {
// 借助于waitgroup实现
g.wg.Wait()
// 如果包装了cancel,也就是context的CancelFunc,执行退出操作
if g.cancel != nil {
g.cancel()
}
return g.err
}

1、借助于waitgroup实现对goroutine阻塞;

2、如果包装了contextCancelFunc,在出错的时候进行退出操作;

3、抛出第一个出错的goroutine的错误信息。

错误的使用

不过工作中发现一个errgroup错误使用的例子

func main() {
eg := errgroup.Group{}
var err error
eg.Go(func() error {
// 处理业务
err = test1()
return err
}) eg.Go(func() error {
// 处理业务
err = test1()
return err
}) if err = eg.Wait(); err != nil {
fmt.Println(err)
}
} func test1() error {
return errors.New("test2")
}

很明显err被资源竞争了

$ go run -race main.go
==================
WARNING: DATA RACE
Write at 0x00c0000801f0 by goroutine 8:
main.main.func2()
/Users/yj/Go/src/Go-POINT/sync/errgroup/main.go:23 +0x97
...

总结

errgroup相比比较简单,不过需要先弄明白waitgroup,context以及sync.Once,主要是借助这几个组件来实现的。

errgroup可以带携带context,如果包装了context,会使用context.WithCancel进行超时,取消或者一些异常的情况

go中errgroup源码解读的更多相关文章

  1. go中panic源码解读

    panic源码解读 前言 panic的作用 panic使用场景 看下实现 gopanic gorecover fatalpanic 总结 参考 panic源码解读 前言 本文是在go version ...

  2. go中waitGroup源码解读

    waitGroup源码刨铣 前言 WaitGroup实现 noCopy state1 Add Wait 总结 参考 waitGroup源码刨铣 前言 学习下waitGroup的实现 本文是在go ve ...

  3. etcd中watch源码解读

    etcd中watch的源码解析 前言 client端的代码 Watch newWatcherGrpcStream run newWatchClient serveSubstream server端的代 ...

  4. java中jdbc源码解读

    在jdbc中一个重要的接口类就是java.sql.Driver,其中有一个重要的方法:Connection connect(String url, java.util.Propeties info); ...

  5. 【原】Spark中Job的提交源码解读

    版权声明:本文为原创文章,未经允许不得转载. Spark程序程序job的运行是通过actions算子触发的,每一个action算子其实是一个runJob方法的运行,详见文章 SparkContex源码 ...

  6. HttpServlet中service方法的源码解读

    前言     最近在看<Head First Servlet & JSP>这本书, 对servlet有了更加深入的理解.今天就来写一篇博客,谈一谈Servlet中一个重要的方法-- ...

  7. AbstractCollection类中的 T[] toArray(T[] a)方法源码解读

    一.源码解读 @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { //size为集合的大小 i ...

  8. go 中 sort 如何排序,源码解读

    sort 包源码解读 前言 如何使用 基本数据类型切片的排序 自定义 Less 排序比较器 自定义数据结构的排序 分析下源码 不稳定排序 稳定排序 查找 Interface 总结 参考 sort 包源 ...

  9. Mybatis源码解读-SpringBoot中配置加载和Mapper的生成

    本文mybatis-spring-boot探讨在springboot工程中mybatis相关对象的注册与加载. 建议先了解mybatis在spring中的使用和springboot自动装载机制,再看此 ...

随机推荐

  1. 牛客编程巅峰赛S1第5场 - 青铜&白银 C.排队 (优先队列,归并排序)

    题意:有\(m\)个窗口,\(n\)个人排队,每个人都有各自的办理时间,只有办理完成窗口才能空出来,后面的人开始办理,求有多少人比后面的人开始办理的早但完成的晚. 题解:我们可以用优先队列来模拟办理, ...

  2. Codeforces 11D A Simple Task 统计简单无向图中环的个数(非原创)

    太难了,学不会.看了两天都会背了,但是感觉题目稍微变下就不会了.dp还是摸不到路子. 附ac代码: 1 #include<iostream> 2 #include<cstdio> ...

  3. Single Round Math sdut3260高精度除以低精度

    做高精度除法,从高位开始除..高位除剩下的我们就*10扔给低一位处理,最终余数是在最低位取模得到的 高精除以高精,我们可以这么做,让除数在后面补零,刚好小于被除数,作若干次减法,减的次数加到商里面 然 ...

  4. JVM实战篇

    1.1 JVM参数 1.1.1 标准参数 -version -help -server -cp 1.1.2 -X参数 非标准参数,也就是在JDK各个版本中可能会变动 -Xint 解释执行 -Xcomp ...

  5. Gym 101170I Iron and Coal(BFS + 思维)题解

    题意:有一个有向图,有些点是煤,有些点是铁,但不会同时有铁和煤.现在我要从1出发,占领可以到达的点.问最少占领几个点能同时拥有一个煤和一个铁(1不用占领). 思路:思路很秀啊.我们从1往外bfs,得到 ...

  6. json-server All In One

    json-server All In One https://github.com/typicode/json-server#getting-started $ npm i -g json-serve ...

  7. 前端 Web 异常监控系统 All In One

    前端 Web 异常监控系统 All In One Sentry https://sentry.io trackjs https://trackjs.com/ rollbar https://rollb ...

  8. HTML5 + JS 网站追踪技术:帆布指纹识别 Canvas FingerPrinting Universally Unique Identifier,简称UUID

    1 1 1 HTML5 + JS  网站追踪技术:帆布指纹识别 Canvas FingerPrinting 1 一般情况下,网站或者广告联盟都会非常想要一种技术方式可以在网络上精确定位到每一个个体,这 ...

  9. React Hooks: useCallback All In One

    React Hooks: useCallback All In One useCallback https://reactjs.org/docs/hooks-reference.html#usecal ...

  10. convert number or string to ASCII in js

    convert number or string to ASCII in js ASCII dictionary generator // const dict = `abcdefghijklmnop ...