go中errgroup源码解读
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中,来实现对使用errgroup的ctx启动的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、如果包装了context的CancelFunc,在出错的时候进行退出操作。
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、如果包装了context的CancelFunc,在出错的时候进行退出操作;
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源码解读的更多相关文章
- go中panic源码解读
panic源码解读 前言 panic的作用 panic使用场景 看下实现 gopanic gorecover fatalpanic 总结 参考 panic源码解读 前言 本文是在go version ...
- go中waitGroup源码解读
waitGroup源码刨铣 前言 WaitGroup实现 noCopy state1 Add Wait 总结 参考 waitGroup源码刨铣 前言 学习下waitGroup的实现 本文是在go ve ...
- etcd中watch源码解读
etcd中watch的源码解析 前言 client端的代码 Watch newWatcherGrpcStream run newWatchClient serveSubstream server端的代 ...
- java中jdbc源码解读
在jdbc中一个重要的接口类就是java.sql.Driver,其中有一个重要的方法:Connection connect(String url, java.util.Propeties info); ...
- 【原】Spark中Job的提交源码解读
版权声明:本文为原创文章,未经允许不得转载. Spark程序程序job的运行是通过actions算子触发的,每一个action算子其实是一个runJob方法的运行,详见文章 SparkContex源码 ...
- HttpServlet中service方法的源码解读
前言 最近在看<Head First Servlet & JSP>这本书, 对servlet有了更加深入的理解.今天就来写一篇博客,谈一谈Servlet中一个重要的方法-- ...
- AbstractCollection类中的 T[] toArray(T[] a)方法源码解读
一.源码解读 @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { //size为集合的大小 i ...
- go 中 sort 如何排序,源码解读
sort 包源码解读 前言 如何使用 基本数据类型切片的排序 自定义 Less 排序比较器 自定义数据结构的排序 分析下源码 不稳定排序 稳定排序 查找 Interface 总结 参考 sort 包源 ...
- Mybatis源码解读-SpringBoot中配置加载和Mapper的生成
本文mybatis-spring-boot探讨在springboot工程中mybatis相关对象的注册与加载. 建议先了解mybatis在spring中的使用和springboot自动装载机制,再看此 ...
随机推荐
- 吉哥系列故事――恨7不成妻 HDU - 4507
题目: 单身! 依然单身! 吉哥依然单身! DS级码农吉哥依然单身! 所以,他生平最恨情人节,不管是214还是77,他都讨厌! 吉哥观察了214和77这两个数,发现: 2+1+4=7 7+7=7*2 ...
- 2017, X Samara Regional Intercollegiate Programming Contest E. Bonuses and Teleports (思维,模拟)
题意:在\(x\)轴上有很多传送点和钻石,当位于传送点上时,可以传送到其他任意传送点(不记操作数),位于钻石上时可以吃掉它,每次可以移动一个单位,问最少多少次可以吃掉所有的钻石. 题解:对于某个位置上 ...
- Linux系统编程【3.1】——编写ls命令
ls命令简介 老规矩,直接在终端输入:man ls (有关于man命令的简介可以参考笔者前期博客:Linux系统编程[1]--编写more命令) 可以看到,ls命令的作用是显示目录中的文件名,它带有可 ...
- 洛谷P1462-通往奥格瑞玛的道路-二分+最短路
洛谷P1462-通往奥格瑞玛的道路 题目描述 在艾泽拉斯,有\(n\)个城市.编号为\(1,2,3,...,n\). 城市之间有\(m\)条双向的公路,连接着两个城市,从某个城市到另一个城市,会遭到联 ...
- 国产网络测试仪MiniSMB - 利用Ctrl+c/Ctrl+v/Ctrl+a快速编辑数据流
国产网络测试仪MiniSMB(www.minismb.com)是复刻smartbits的IP网络性能测试工具,是一款专门用于测试智能路由器,网络交换机的性能和稳定性的软硬件相结合的工具.可以通过此以太 ...
- docker+prom+grafana+altermanager
docker基础 docker run -it --name centos -v $HOME:/tmp -p 8080:8080 centos docker inspect container #查看 ...
- python之字符串split和rsplit的方法
1.描述 split()方法通过指定分隔符对字符串进行切片,如果参数num有指定值,则分隔num+1个子字符串,默认分隔符为所有空字符,包括空格.换行(\n).制表符(\t)等 rstrip()方法通 ...
- C# 类 (1)
通常每个类都会单独定义在自己的文件里,方便区分 Class 里面定义了 变量 属性 方法 实例化这个Class,得到一个对象,然后可以使用这个对象的变量 属性和方法 属性 Properties 像是一 ...
- HDU 6395 Sequence(分段矩阵快速幂)题解
题意: 已知\(A,B,C,D,P,n\)以及 \[\left\{ \begin{aligned} & F_1 = A \\ & F_2 = B\\ & F_n = C*F_{ ...
- HDU 3341 Lost's revenge (AC自动机 + DP + 变进制/hash)题解
题意:给你些分数串,给你一个主串,主串每出现一个分数串加一分,要你重新排列主串,最多几分 思路:显然这里开$40^4$去状压内存不够.但是我们自己想想会发现根本不用开那么大,因为很多状态是废状压,不是 ...