WaitGroup用于等待一组线程的结束。父线程调用Add方法来设定应等待的线程的数量。每个被等待的线程在结束时应调用Done方法。同时,主线程里可以调用Wait方法阻塞至所有线程结束。
但在使用时,也有一些问题需要注意,请看本文的详细分解。
另外,WaitGroup的使用场景十分有限,为什么呢?具体原因,请看本文的总结部分的分析。
主要函数
func (*WaitGroup) Add
Add方法向内部计数加上delta,delta可以是负数;如果内部计数器变为0,Wait方法阻塞等待的所有协程都会释放,如果计数器小于0,则调用panic。注意Add加上正数的调用应在Wait之前,否则Wait可能只会等待很少的协程。一般来说本方法应在创建新的协程或者其他应等待的事件之前调用。
func (wg *WaitGroup) Done()
Done方法减少WaitGroup计数器的值,应在协程的最后执行。
func (wg *WaitGroup) Wait()
Wait方法阻塞直到WaitGroup计数器减为0。
编程实战
等待某个协程结束
小结
要注意Add和Done函数一定要配对,否则可能发生死锁,所报的错误信息如下:
fatal error: all goroutines are asleep - deadlock!
等待协程组结束
运行以上程序,输出如下:
无论运行多少次,都能保证All goroutines finished!这一句在最后一行输出,这说明,Wait()函数等了所有协程都结束自己才返回。
小结
以上程序通过WaitGroup提供的三个同步接口,实现了等待一个协程组完成的同步操作。在实现时要注意:
* Add的参数N必须和创建的goroutine的数量相等,否则会报出死锁的错误信息
* 另外,sayHello()函数中要传递WaitGroup的指针呢?在该结构的实现源码中已经有讲解:
A WaitGroup must not be copied after first use.
就是说,该结构定义后就不能被复制,所以这里要使用指针。
总结
通过WaitGroup提供的三个函数:Add,Done,Wait,可以轻松实现等待某个协程或协程组完成的同步操作。但在使用时要注意:
Add的数量和Done的调用数量必须相等。
另外,就是WaitGroup结构一旦定义就不能复制的原因。
WaitGroup在需要等待多个任务结束再返回的业务来说还是很有用的,但现实中用的更多的可能是,先等待一个协程组,若所有协程组都正确完成,则一直等到所有协程组结束;若其中有一个协程发生错误,则告诉协程组的其他协程,全部停止运行(本次任务失败)以免浪费系统资源。
该场景WaitGroup是无法实现的,那么该场景该如何实现呢,就需要用到通知机制,其实也可以用channel来实现,具体的解决办法,请看后续的文章。
这样说来,WaitGroup的使用场景是有限的。