[Golang1.20 source code reading] sync/waitgroup.go

// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sync

import (
	"internal/race"
	"sync/atomic"
	"unsafe"
)

// A WaitGroup waits for a collection of goroutines to finish.
// The main goroutine calls Add to set the number of
// goroutines to wait for. Then each of the goroutines
// runs and calls Done when finished. At the same time,
// Wait can be used to block until all goroutines have finished.
// WaitGroup等待goroutines的集合完成。主goroutine调用Add来设置要等待的goroutine的数量。
// 然后,每个goroutine都会运行,并在完成时调用Done。同时,可以使用Wait来阻止,直到所有goroutine都完成。
// A WaitGroup must not be copied after first use.
// 首次使用后不得复制WaitGroup。
// In the terminology of the Go memory model, a call to Done
// “synchronizes before” the return of any Wait call that it unblocks.
// 在Go内存模型的术语中,对Done的调用在其取消阻止的任何Wait调用返回之前“同步”。
type WaitGroup struct {
	noCopy noCopy // 禁止拷贝
	// 状态位
	state atomic.Uint64 // high 32 bits are counter, low 32 bits are waiter count. 高32位是计数器,低32位是等待者数量。
	sema  uint32 // 信号量
}

// Add adds delta, which may be negative, to the WaitGroup counter.
// If the counter becomes zero, all goroutines blocked on Wait are released.
// If the counter goes negative, Add panics.
// Add将可能为负的delta添加到WaitGroup计数器中。如果计数器变为零,则在Wait上阻止的所有goroutine都将被释放。如果计数器为负数,Add会恐慌。
// Note that calls with a positive delta that occur when the counter is zero
// must happen before a Wait. Calls with a negative delta, or calls with a
// positive delta that start when the counter is greater than zero, may happen
// at any time.
//请注意,计数器为零时发生的具有正增量的调用必须在Wait之前发生。具有负增量的调用,或当计数器大于零时开始的具有正增量的调用可能随时发生。
// Typically this means the calls to Add should execute before the statement
// creating the goroutine or other event to be waited for.
// If a WaitGroup is reused to wait for several independent sets of events,
// new Add calls must happen after all previous Wait calls have returned.
// See the WaitGroup example.
// 通常,这意味着对Add的调用应该在创建goroutine或其他待等待事件的语句之前执行。
// 如果重用一个WaitGroup来等待多个独立的事件集,则必须在所有以前的wait调用都返回后才进行新的Add调用。请参阅WaitGroup示例。
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)
	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.
		// 第一个增量必须与“Wait”同步。需要将其建模为读取,因为从0开始可能会有几个并行的wg.counter转换。
		race.Read(unsafe.Pointer(&wg.sema))
	}
	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.
	// 当等待者>0时,此goroutine已将计数器设置为0。现在状态不可能同时发生突变:
	//-加法不能与Wait同时发生,
	//-如果Wait看到counter==0,它不会增加等待程序。
	// 仍然要做一个廉价的健全性检查来检测WaitGroup的滥用。
	if wg.state.Load() != state {
		panic("sync: WaitGroup misuse: Add called concurrently with Wait")
	}
	// Reset waiters count to 0. 将等待者计数重置为0。
	wg.state.Store(0)
	for ; w != 0; w-- {
		runtime_Semrelease(&wg.sema, false, 0)
	}
}

// Done decrements the WaitGroup counter by one.
// Done将WaitGroup计数器递减一。
func (wg *WaitGroup) Done() {
	wg.Add(-1)
}

// Wait blocks until the WaitGroup counter is zero.
// Wait块,直到WaitGroup计数器为零。
func (wg *WaitGroup) Wait() {
	if race.Enabled {
		race.Disable()
	}
	for {
		// 获取状态位
		state := wg.state.Load()
		v := int32(state >> 32)
		w := uint32(state)
		if v == 0 {
			// Counter is 0, no need to wait. 计数器为0,无需等待。
			if race.Enabled {
				race.Enable()
				race.Acquire(unsafe.Pointer(wg))
			}
			return
		}
		// Increment waiters count. 增加等待者数量。
		if wg.state.CompareAndSwap(state, state+1) {
			if race.Enabled && w == 0 {
				// Wait must be synchronized with the first Add.
				// Need to model this is as a write to race with the read in Add.
				// As a consequence, can do the write only for the first waiter,
				// otherwise concurrent Waits will race with each other.
				// Wait必须与第一次Add同步。需要将其建模为在Add中与读取竞争的写入。因此,只能为第一个等待者写,否则并发的等待者将相互竞争。
				race.Write(unsafe.Pointer(&wg.sema))
			}
			runtime_Semacquire(&wg.sema)
			if wg.state.Load() != 0 {
				panic("sync: WaitGroup is reused before previous Wait has returned")
			}
			if race.Enabled {
				race.Enable()
				race.Acquire(unsafe.Pointer(wg))
			}
			return
		}
	}
}

おすすめ

転載: blog.csdn.net/qq2942713658/article/details/132431184