WaitGroup 是常用的 Go 同步原语之一,用来做任务编排。它要解决的就是并发-等待的问题: 现在有一个 goroutine A 在检查点 ( checkpoint ) 等待一组 goroutine 全部完成它们的任务,如果这些 goroutine 还没全部完成任务,那么 goroutine A 就会被阻塞在检查点,直到所有的 goroutine 都完成任务后才能继续执行。
我们来看一个使用 WaitGroup 的场景。
比如,我们要完成一个大任务,需要使用并行的 goroutine 执行三个小任务,只有这三个小任务都完成了,才能执行后面的任务。如果通过轮询的方式定时询问三个小任务是否完成,则会存在两个问题:一是性能比较低,因为三个小任务可能早就完成了,却要等很长时间才能被轮询到;二是会有很多无谓的轮询,空耗CPU资源。
这个时候使用 WaitGroup 同步原语就比较有效了,它可以阻塞等待的 goroutine,等到三个小任务都完成了,再即时唤醒它们。 其实,很多操作系统和编程语言都提供了类似的同步原语,比如 Linux 中的 barrier、Pthread(POSIX 线程)中的 barrier、C ++ 中的 std::barrier、Java 中的 CyclicBarrier 和 CountDownLatch 等。
1. WaitGroup 的使用方法
在 Go 官方提供的同步原中,最常用的几个类型使用起来很简单,这是很不容易的设计。WaitGroup 就是简单且常用的同步原语之一,它只有三个方法。
- Add(delta int): 给 WaitGroup 的计数值增加一个数值, delta 可以是负数。当 WaitGroup 的计数值减小到 0 时,任何阻塞在 Wait( )方法上的 goroutine 都会被解除封印,不再阻塞,可以继续执行。如果计数器的值为负数,则会出现 panic。
- Done( ):表示一个 goroutine 完成了任务,WaitGroup 的计数值减 1。
- Wait( ): 此方法的调用者会被阻塞,直到 WaitGroup 的计数值减小到 0。
WaitGroup 的功能就是等待一组 goroutine 都完成任务。一般主 g