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的情况
        • 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进行重用