golang并发执行多任务(网络i/o、磁盘i/o、内存i/o等),并聚合收集多任务执行结果与执行状态(如任务组执行失败,将返回首个必要成功任务的错误信息, 且会立即停止后续任务的运行)。
Quick Start
& Best Practice
使用: go get github.com/mlee-msl/taskgroup
- 并发安全的执行多个任务
- 将多个任务的执行结果与执行状态进行聚合
- 通过扇出/扇入模式,结合线程安全
channel
实现高效协程间通信 - 多任务复用(共享)同一协程,避免了因协程频繁创建或销毁带来的开销,一定程度上也减少了上下文切换(特别是,内核线程与用户协程间的切换)的频次
early-return
,当出现必要成功的任务失败时,将停止执行所有goroutine
上还未启动的所有其他任务NOTEs,当所有任务都设置为非必要成功时,即可退化为
errgroup
包的使用场景
vs官方扩展库errgroup
- errgroup 没有任务添加阶段,直接会使用协程执行指定的任务
可通过限制协程数量上限,控制并发量(指定
buffer size
的channel
实现),当协程数达到上限时,需要等待现有任务执行结束,然后开启新的协程,会增加协程创建或销毁的成本另外,当协程量不指定时(默认协程量不受限),随着任务量的增加,会增加内核态线程与用户态协程间的上下文切换成本
- errgroup可支持带有取消Context的模式,但实际上,该种模式下仍需要所有执行任务的
goroutine
执行完毕(每个任务都会绑定到新的goroutine
上) - 仅返回了首个出现错误的接口(任务)状态,未将所有接口(任务)的执行结果(或执行状态)进行收集
- 当使用errgroup.WithContext时,出现错误
cancel
后,需要同步(如,atomic
同步原语)后续协程(因任务阻塞还未开始执行的协程)不再启动(后续执行已无意义,业务层面已选择了带取消的上下文执行方式), 也确保避免了出现内存泄露(协程泄露等)等可能的问题 - 增加一个默认的
context.Context
,和errgroup.WithContext
的处理逻辑保持一致,统一使用context.Context
处理错误 (功能实现上,整体可能会更加优雅点)
cpu
性能
go test -benchmem -run=^$ -bench ^BenchmarkTaskGroup$ -cpu='1,2,4,8,16' [-benchtime=10x|-benchtime=1s] -count=3 -cpuprofile='cpu.pprof' .
内存性能
go test -benchmem -run=^$ -bench ^BenchmarkTaskGroup$ -cpu='1,2,4,8,16' [-benchtime=10x|-benchtime=1s] -count=3 -memprofile='mem.pprof' .
阻塞性能
go test -benchmem -run=^$ -bench ^BenchmarkTaskGroup$ -cpu='1,2,4,8,16' [-benchtime=10x|-benchtime=1s] -count=3 -blockprofile='block.pprof' .
锁性能
go test -benchmem -run=^$ -bench ^BenchmarkTaskGroup$ -cpu='1,2,4,8,16' [-benchtime=10x|-benchtime=1s] -count=3 -mutexprofile='mutex.pprof' .
taskgroup
errgroup
可以看出,在增加任务执行结果聚合、失败快速返回的能力前提下:
当并发任务量不多时,前者相较于后者的综合性能提升还不明显;
当并发任务量较多时,前者相较于后者的综合性能表现提升较大;
深度验证后发现,随着并发任务量的增加,其综合性能表现的差距将会越明显。
- 核心功能的测试用例均采用
Fuzz Test
模糊测试,并测试通过 - 包中提供的核心功能,均有
Example Test
样例用例,并执行通过,更多详情