18 | 分组操作:处理一组子任务,该用什么并发原语?
晁岳攀
该思维导图由 AI 生成,仅供参考
你好,我是鸟窝。
共享资源保护、任务编排和消息传递是 Go 并发编程中常见的场景,而分组执行一批相同的或类似的任务则是任务编排中一类情形,所以,这节课,我专门来介绍一下分组编排的一些常用场景和并发原语,包括 ErrGroup、gollback、Hunch 和 schedgroup。
我们先来学习一类非常常用的并发原语,那就是 ErrGroup。
ErrGroup
ErrGroup是 Go 官方提供的一个同步扩展库。我们经常会碰到需要将一个通用的父任务拆成几个小任务并发执行的场景,其实,将一个大的任务拆成几个小任务并发执行,可以有效地提高程序的并发度。就像你在厨房做饭一样,你可以在蒸米饭的同时炒几个小菜,米饭蒸好了,菜同时也做好了,很快就能吃到可口的饭菜。
ErrGroup 就是用来应对这种场景的。它和 WaitGroup 有些类似,但是它提供功能更加丰富:
和 Context 集成;
error 向上传播,可以把子任务的错误传递给 Wait 的调用者。
接下来,我来给你介绍一下 ErrGroup 的基本用法和几种应用场景。
基本用法
golang.org/x/sync/errgroup 包下定义了一个 Group struct,它就是我们要介绍的 ErrGroup 并发原语,底层也是基于 WaitGroup 实现的。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
本文介绍了并发编程中的常见场景和并发原语,重点介绍了ErrGroup的基本用法和几种应用场景,以及一些扩展库如gollback、Hunch和schedgroup。ErrGroup是Go官方提供的一个同步扩展库,用于将大任务拆分成多个小任务并发执行,提高程序的并发度。文章还介绍了一些其他实用的Group并发原语,如SizedGroup和ErrSizedGroup,它们提供了控制并发goroutine数量的功能。此外,还介绍了gollback和Hunch提供的一些方法,以及schedgroup的使用方法。通过实际例子和详细讲解,帮助读者全面了解了ErrGroup的使用方法和应用场景,以及相关的扩展库。整体来说,本文内容涵盖了并发编程中常见的业务场景和处理方式,对于需要处理并发编程的读者具有一定的参考价值。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Go 并发编程实战课》,新⼈⾸单¥59
《Go 并发编程实战课》,新⼈⾸单¥59
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(16)
- 最新
- 精选
- 大漠胡萝卜看了这篇文章还是收获巨大,以前不知道,也没用用过。 首先是,ErrGroup, 官方的实现,bilibili/errgroup,neilotoole/errgroup,facebookgo/errgroup 其它实用的 Group 并发原语SizedGroup/ErrSizedGroup gollback各种实现,以前在nodejs中经常使用,没想到golang也有实现。 类似的还有Hunch实现。 另外,如果针对定时器比较多的情况,可以使用 schedgroup
作者回复: ������������
2020-11-235 - mbc老师,这种和用普通的go协程或者channel去完成一组任务的编写有啥不一样吗?好处是啥
作者回复: 这一组并发原语只是提供了便利的方法,底层还是用gotoutinr和channel实现的
2020-11-2822 - 东泽文章中多次出现的“让子任务中断执行”,这里的语义是中断子goroutine的执行还是在子goroutine中用类似select <-ctx.Done()的方式在执行任务前判断任务是否应该进行。
作者回复: M第二种
2021-12-101 - moooofly```go ... for i := 0; i < total; i++ { // 并发一万个goroutine执行子任务,理论上这些子任务都会加入到Group的待处理列表中 go func() { g.Go(func(ctx context.Context) error { atomic.AddInt64(&count, 1) return nil }) }() } ... ``` 这个地方有点不理解,g.Go() 不就是创建了子 goroutine 了么,为啥这里还要使用 go func() {...} ;我看文章中其他类似地方都没有这么用,只有这里是这样~
作者回复: 模拟一万个goroutine并发调用g.Go
2020-11-2421 - MyronMeng我们应该担心【更进一步,返回所有子任务的错误】这一节: func main() { var g errgroup.Group var result = make([]error, 3) // 启动第一个子任务,它执行成功 g.Go(func() error { time.Sleep(5 * time.Second) fmt.Println("exec #1") result[0] = nil // 保存成功或者失败的结果 return nil }) .... } 这段代码多个 goroutine 写同一个 slice 导致 panic 吗?
作者回复: 不会
2023-04-27归属地:广东 - 愤怒的小猥琐errorgroup 49-55 行 为什么要用 go func(){} 去执行 g.Wait() 和 close(ch) 呢,按理说不是应该堵塞等待完成吗?
作者回复: 嗯,是的。
2022-06-13归属地:北京 - 秋晨任务执行流水线 Pipeline 的例子第44行,如果处理失败,返回err,当前的goroutine是否会退出呢,如果会退出的话,开启的goroutine数量少于待处理的数据量的话,会不会出现待处理的数据无goroutine处理
作者回复: 不会。我建议你遇到此类问题跟进去看一下Go方法的实现就清楚了
2021-06-15 - 皮卡丘cancel,失败的子任务可以 cancel 所有正在执行任务,这句话是不是有点绝对,ctx只是传递信号,如果cancel时,还有子任务的gouroutine没有调度或者内部还没有运行到监听信号才会被cancel掉
作者回复: 所以说是“可以”������,这依赖子goroutine自己想不想cancel
2020-11-20 - 老王使用 errgroup 保存多个错误的代码,运行会报 data race 吧2020-12-1614
- 伟伟package main import ( "context" "fmt" "time" "golang.org/x/sync/errgroup" ) type Data struct { } func getData() (*Data, error) { time.Sleep(3 * time.Second) return &Data{}, nil } func main() { c, cancel := context.WithCancel(context.Background()) defer cancel() g, ctx := errgroup.WithContext(c) datas := make(chan *Data, 10) g.Go(func() error { // 业务逻辑 data, err := getData() if err != nil { return err } select { case <-ctx.Done(): return ctx.Err() default: } datas <- data return nil }) go func() { time.Sleep(1 * time.Second) cancel() }() err := g.Wait() if err == nil { fmt.Println("success") return } fmt.Println("fail", err) } 这里理解的是取消做在业务相关层2020-11-263
收起评论