WaitGroup的Add()方法

50 阅读2分钟

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)
	}
}