WaitGroup并发控制
sync.WaitGroup内部维护着一个计数器,计数器的值可以增加和减少。
例如当我们启动了N 个并发任务时,就通过Add()将计数器值增加N。
每个任务完成时通过调用Done()方法将计数器减1。
通过调用Wait()来等待并发任务执行完,当计数器值为0时,表示所有并发任务已经完成。
const N = 10
var wg = &sync.WaitGroup{}
func main() {
for i := 0; i < N; i++ {
//wg.Add(1) 正确写法
go func(i int) {
wg.Add(1)
println(i)
defer wg.Done()
}(i)
}
wg.Wait()
}
结果
结果不唯一,代码存在风险, 所有go未必都能执行到
这是使用WaitGroup经常犯下的错误!请各位同学多次运行就会发现输出都会不同甚至又出现报错的问题。 这是因为go
执行太快了,导致wg.Add(1)
还没有执行main函数就执行完毕了。
底层原理
type WaitGroup struct {
noCopy noCopy
state1 [3]uint32 // uint32数组一共12个字节,前8个即uint64记录 高8位记录需要等待的数量 低8位正在等待 的数量 ,后4个字节存储信号量,用于唤醒
}
1.核心原理就是通过之前说的64位的uint64来进行计数,采用高位记录需要Done的数量,低位记录Wait的数量,然后排队休眠等待唤醒
2.如果发现当前count>0则 Wait的goroutine会进行排队
3.任务完成后的goroutine则进行Done操作,直到count==0,则完成,就唤醒所有因为wait操作睡眠的goroutine