Golang1.17源码分析之WaitGroup
Golang1.17 学习笔记012
WaitGroup,可理解为 Wait-Goroutine-Group,即等待一组 goroutine 结束
源码包:sync/waitgroup.go
type WaitGroup struct {
noCopy noCopy
// 64-bit value: high 32 bits are counter, low 32 bits are waiter count.
// 64-bit atomic operations require 64-bit alignment, but 32-bit
// compilers do not ensure it. So we allocate 12 bytes and then use
// the aligned 8 bytes in them as state, and the other 4 as storage
// for the sema.
state1 [3]uint32
}
state1 是三个值分别代表:
-
counter: 当前还未执行结束的 goroutine 计数器
-
waiter count: 等待 goroutine-group 结束的 goroutine 数量,即有多少个等候者
-
semaphore: 信号量
12.1 add
add 做了两件事,一是将 delta 值加入到 counter 中;二是如果 counter 为 0 时,根据 waiter 数值释放的
信号量,把等待的 goroutine 全部唤醒
func (wg *WaitGroup) Add(delta int) {
statep, semap := wg.state()
state := atomic.AddUint64(statep, uint64(delta)<<32)
v := int32(state >> 32)
w := uint32(state)
if v < 0 {
panic("sync: negative WaitGroup counter")
}
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.
if *statep != state {
panic("sync: WaitGroup misuse: Add called concurrently with Wait")
}
// Reset waiters count to 0.
*statep = 0
for ; w != 0; w-- {
runtime_Semrelease(semap, false, 0)
}
}
12.2 wait
wait 也做两件事:一是累加waiter, 二是阻塞等待信号量
func (wg *WaitGroup) Wait() {
statep, semap := wg.state()
for {
state := atomic.LoadUint64(statep)
v := int32(state >> 32)
w := uint32(state)
if v == 0 {
// Counter is 0, no need to wait.
return
}
// Increment waiters count.
if atomic.CompareAndSwapUint64(statep, state, state+1) {
runtime_Semacquire(semap)
if *statep != 0 {
panic("sync: WaitGroup is reused before previous Wait has returned")
}
if race.Enabled {
race.Enable()
race.Acquire(unsafe.Pointer(wg))
}
return
}
}
}
12.3 done
done 只做一件事,把 counter 减 1
func (wg *WaitGroup) Done() {
wg.Add(-1)
}