Go语言中两种方式来等待其他 goroutine 执行结束,一种是使用共享的变量,另一种是使用 channel,具体的就不再细说,感兴趣的可以看一下上一篇文章。这篇文章主要说 Go 中给我们封装好的方式来等待其他 goroutine 执行结束。
WaitGroup
话不多说,直接看一个最简单的使用:
WaitGroup
这么简单嘛?是的,就是这么简单。而且函数名也非常见名知意,Add 就是添加一个需要等待的协程数量,Done 就是一个协程执行结束,Wait 就是等待。
虽然这么简单,但是实际应用时还是要注意一些细节的。Add 和 Wait 不能并发,也就是说,一定要先把需要等待的数量添加好,之后再等待,不然程序会 panic。Add 之后的总数不能是负数,不然也会 panic。对于 Add、Done、Wait 的操作必须是同一个对象,也就是说,我们如果需要将 wg 对象传给其他函数,就需要使用指针了。
接下来我们一起看一下 源码 :
struct
上面是 WaitGroup 的 结构体 ,这里我们可以知道,我们最多同时可以等待2^32个 goroutine,所以不用担心数量问题了。
我们再看 Add 接口代码:
Add
这里大致讲一下流程,首先就是获取计数器,如果计数器小于零,程序 panic,如果 Add 和 Wait 并发调用,程序 panic,最后判断一下是否添加成功。其实说白了就是一个计数器添加的过程。
我们接着看 Done 接口:
Done
没错就是这么简单,将计数器减一就好了。
最好我们看一下 Wait 接口:
Wait
也是非常的简单,主要就是一个 for 循环,获取计数器,判断一下如果计数器为0,等待结束,不为0,那么增加等待计数。runtime_Semacquire(semap) 这一行代码目的是作为一个简单的sleep 原语 ,以供同步使用。
看到这里大家应该基本了解了 WaitGroup 是如何实现的了,先不管具体的细节,大体上看,就是一个计数器的方式,添加一个我就+1,结束一个我就-1。这里并没有用到 channel,并不是说不用 channel 就不好了, WaitGroup 也是有缺点的:
WaitGroup不能保证多个 goroutine 执行次序
WaitGroup无法指定固定的goroutine数目
这两点大家需要注意,WaitGroup 只管等 goroutine 执行结束,其他的逻辑关系它并不关心。但是实际情况我们很多时候会用到对其他协程的计数,或者说对某个协程或者某几个协程的执行过程是关心的,那么这个时候就不适合了,channel 就更加适合了,所以说,通过了通信来共享内存。
其实对象现有的 WaitGroup 我们是可以改进的,我们可以增加使用 channel 来得知某一个协程的执行状态或者执行到哪里,这里我就不细说了,如果大家真的有这种需求需要自己动手写代码,还是需要很小心的,channel 处理不好是容易 dead lock 的。
感谢阅读,祝大家生活愉快!