go sync.WaitGroup
1、数据结构
type WaitGroup struct {
noCopy noCopy
state atomic.Uint64 // high 32 bits are counter, low 32 bits are waiter count.
sema uint32
}
计数器:原子变量,高32位用于为协程计数,低32位为等待计数(被Wait阻塞等待)。
信号量:用于阻塞协程。
2、Add(x)
// 简化版
// Add adds delta, which may be negative, to the [WaitGroup] counter.
// If the counter becomes zero, all goroutines blocked on [WaitGroup.Wait] are released.
// If the counter goes negative, Add panics.
func (wg *WaitGroup) Add(delta int) {
state := wg.state.Add(uint64(delta) << 32)
v := int32(state >> 32)
w := uint32(state)
if v > 0 || w == 0 {
return
}
// Reset waiters count to 0.
wg.state.Store(0)
for ; w != 0; w-- {
runtime_Semrelease(&wg.sema, false, 0)
}
}
通过原子操作对协程计数器加减,若协程计数变为0,则将等待计数设置为0,并释放所有被信号量阻塞的协程。
3、Done()
// 简化版
// Done decrements the [WaitGroup] counter by one.
func (wg *WaitGroup) Done() {
wg.Add(-1)
}
计数器-1
4、Wait()
// 简化版
// Wait blocks until the [WaitGroup] counter is zero.
func (wg *WaitGroup) Wait() {
for {
state := wg.state.Load()
v := int32(state >> 32)
w := uint32(state)
if v == 0 {
// Counter is 0, no need to wait.
return
}
// Increment waiters count.
if wg.state.CompareAndSwap(state, state+1) {
runtime_Semacquire(&wg.sema)
return
}
}
}
若协程计数为0,则直接跳过。
若协程计数非0,则通过CAS将等待计数+1,并通过信号量阻塞协程。