WaitGroup
sync.WaitGroup允许goroutine在继续执行之前等待一个或多个goroutine执行完毕,它的定义如下:
type WaitGroup struct {
noCopy noCopy
state atomic.Uint64 // high 32 bits are counter, low 32 bits are waiter count.
sema uint32
}
其中noCopy是防止WaitGroup在第一次使用前被copy,sema是信号量,用来阻塞或者唤醒等待的goroutine,最为关键的是state字段,它的高32位表示计数器,低32位表示当前等待的goroutine数量
Add()方法
func (wg *WaitGroup) Add(delta int) {
if race.Enabled {
if delta < 0 {
// Synchronize decrements with Wait.
race.ReleaseMerge(unsafe.Pointer(wg))
}
race.Disable()
defer race.Enable()
}
//使用位运算计算计数器的值
state := wg.state.Add(uint64(delta) << 32)
//获取计数器的具体值
v := int32(state >> 32)
//获取当前等待者的值
w := uint32(state)
//如果race.Enabled为真且delta是正数且计数器的值等于delta,说明是第一次做Add操作,这个时候需要确保不会有其他goroutine调用Wait方法来等待,做竞态检测
if race.Enabled && delta > 0 && v == int32(delta) {
// The first increment must be synchronized with Wait.
// Need to model this as a read, because there can be
// several concurrent wg.counter transitions from 0.
race.Read(unsafe.Pointer(&wg.sema))
}
//如果计数器的值小于0,panic
if v < 0 {
panic("sync: negative WaitGroup counter")
}
//如果等待者的数量不为0,且delta大于0,并且计数器的值等于delta(第一次Add),说明Add和Wait并发执行,panic
if w != 0 && delta > 0 && v == int32(delta) {
panic("sync: WaitGroup misuse: Add called concurrently with Wait")
}
//正常情况,直接返回
if v > 0 || w == 0 {
return
}
// This goroutine has set counter to 0 when waiters > 0.
// Now there can't be concurrent mutations of state:
// - Adds must not happen concurrently with Wait,
// - Wait does not increment waiters if it sees counter == 0.
// Still do a cheap sanity check to detect WaitGroup misuse.
//v = 0的情况,wg.state.Load() != state说明有并发的Wait修改了w的值,painc
if wg.state.Load() != state {
panic("sync: WaitGroup misuse: Add called concurrently with Wait")
}
// Reset waiters count to 0.
//将等待者的值设置为0, 并且调用runtime_Semrelease唤醒所有等待的goroutine
wg.state.Store(0)
for ; w != 0; w-- {
runtime_Semrelease(&wg.sema, false, 0)
}
}