WaitGroup
- 作用
- 控制等待并发结束
- 结构(1.20)
- nocopy nocopy
- state atomic.uint64
- 前32位存放计算器,即一般调用add添加了多少个等待的goruntine
- 后32位存放等待数量,即有多少个wait方法调用,每次调用只要计算器不为0,将通过cas方式进行增加
- sema uint32
- 作为信号量,用于唤醒wait函数触发的阻塞挂起操作
- 结构(1.17及前)
- nocopy nocopy
- state1 [3]uint32
- 64位对齐时前8个字节保存state,后4个字节保存sema
- 非对齐时,前4个字节保存信号量,后8个字节保存state
- 结构(1.20前)
- nocopy nocopy
- state1 uint64
- state2 uint32
- 实现
- Add(delta int)
- 1. 增加计数器
- 2. 检查计数器是否小于0,小于0即panic
- 3.检查等待数量是否大于0,同时检查当前增加的增量是否大于0,并且原来的计算器是否为0(delta > 0 && v ==
int32(delta))。如果是则panic
- panic原因
- 由于Wait()函数中可以看出计数器为0时,由于直接return,而不会导致等待数量加一而出现非0情况。这种情况出现证明出现并发调用add和wait的情况
- panic原因
- 4. 检查计数器是否为0或者等待数量是否为0,是的话直接返回
- 5. 否则继续检查状态state是否已经被修改,是则panic,即发生并发问题,wait不能与add并发
- 6. 到这里证明只剩等待的数量没有处理了,设置state为0
- 7. 遍历等待数量,释放信号量(runtime_Semrelease),进行扫尾工作
- Done()
- 实际调用Add(-1)
- Wait()
- 1. 判断计数器是否为0,0则无需等待直接返回
- 2. 通过cas增加等待数量,增加成功后,即调用信号量acquire等待信号量的通知(runtime_Semacquire),收到信号量后被唤醒继续后续扫尾操作
- 3. 检查state是否为0,如果不为0,即出现这个wait还没结束,已经有其他wait进行增加,或者add调用,出现并发问题,panic,即还没有完成wait就对waitgroup进行重用