一、前言
Go语言在设计上对同步(Synchronization,数据同步和线程同步)提供大量的支持,比如 goroutine和channel同步原语,库层面有
- sync:提供基本的同步原语(比如Mutex、RWMutex、Locker)和 工具类(Once、WaitGroup、Cond、Pool、Map)
- sync/atomic:提供变量的原子操作(基于硬件指令 compare-and-swap)
-- 引用自《Golang package sync 剖析(一): sync.Once》
上一期中,我们介绍了 sync.Once
如何保障 exactly once
语义,本期文章我们介绍 package sync
下的另一个工具类:sync.WaitGroup
。
二、为什么需要 WaitGroup
?
想象一个场景:我们有一个用户画像服务,当一个请求到来时,需要
- 从 request 里解析出 user_id 和 画像维度参数
- 根据 user_id 从 ABCDE 五个子服务(数据库服务、存储服务、rpc服务等)拉取不同维度的信息
- 将读取的信息进行整合,返回给调用方
假设 ABCDE 五个服务的响应时间 p99 是 20~50ms 之间。如果我们顺序调用 ABCDE 读取信息,不考虑数据整合消耗时间,服务端整体响应时间 p99 是:
sum(A, B, C, D, E) => [100ms, 250ms]
先不说业务上能不能接受,响应时间上显然有很大的优化空间。最直观的优化方向就是,取数逻辑的总时间消耗:
sum(A, B, C, D, E) -> max(A, B, C, D, E)
具体到 coding 上,我们需要并行调用 ABCDE 五个子服务,待调用全部返回以后,进行数据整合。如何保障全部
返回呢?
此时,sync.WaitGroup
闪耀登场。
三、WaitGroup
用法
官方文档对 WaitGroup 的描述是:一个 WaitGroup 对象可以等待一组协程结束
。使用方法是:
- main协程通过调用
wg.Add(delta int)
设置worker协程的个数,然后创建worker协程; - worker协程执行结束以后,都要调用
wg.Done()
; - main协程调用
wg.Wait()
且被block,直到所有worker协程全部执行结束后返回。
这里先看一个典型的例子:
// src/cmd/compile/internal/ssa/gen/main.go
func main() {
// 省略部分代码 ...
var wg sync.WaitGroup
for _, task := range tasks {
task := task
wg.Add(1)
go func() {
task()
wg.Done()
}()
}
wg.Wait()
// 省略部分代码...
}
这个例子具备了 WaitGroup
正确使用的大部分要素,包括:
查看完整版(【golang】sync.WaitGroup详解)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。