// 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
}
}
}
【Golang1.20源码阅读】sync/waitgroup.go
原创
©著作权归作者所有:来自51CTO博客作者深漂小码哥的原创作品,请联系作者获取转载授权,否则将追究法律责任
提问和评论都可以,用心的回复会被更多人看到
评论
发布评论
相关文章
-
Apache Doris 聚合函数源码阅读与解析|源码解读系列
Apache Doris Active Contributor 隐形通过本文记录下对源码的理解,以方便新人快速上手源码开发。
Apache Doris 数据库 大数据 数据分析 数据仓库 -
【Golang1.20源码阅读】sync/rwmutex.go
【代码】【Golang1.20源码阅读】sync/rwmutex.go。
golang 信号量 Go sed -
【Golang1.20源码阅读】sync/mutex.go
【代码】【Golang1.20源码阅读】sync/mutex.go。
golang 开发语言 互斥 互斥对象 ide -
【Golang1.20源码阅读】strings/compare.go
【代码】【Golang1.20源码阅读】strings/compare.go。
golang 字符串 Go 比较运算符 -
【Golang1.20源码阅读】runtime/chan.go
【代码】【Golang1.20源码阅读】chan.go。
golang sed 堆栈 Go -
【Golang1.20源码阅读】context/context.go
【代码】【Golang1.20源码阅读】context/context.go。
golang 开发语言 后端 sed User -
【Golang1.20源码阅读】runtime/map.go
【代码】【Golang1.20源码阅读】runtime/map.go。
golang 开发语言 后端 Go 数组 -
【Golang1.20源码阅读】runtime/select.go
【代码】【Golang1.20源码阅读】runtime/select.go。
golang 开发语言 后端 堆栈 数组 -
【Golang1.20源码阅读】runtime/slice.go
【代码】【Golang1.20源码阅读】slice.go。
golang 开发语言 初始化 ci -
【Golang1.20源码阅读】math/const.go
golang 数学常量极限值
golang Powered by 金山文档 ide Max 浮点型 -
【Golang1.20源码阅读】builtin/builtin.go
【Golang源码阅读】builtin/builtin.go
golang Powered by 金山文档 Go 字符串 ci -
【Golang1.20源码阅读】bytes/buffer.go
【代码】【Golang1.20源码阅读】bytes/buffer.go。
golang ci 字节数 字符串 -
【Golang1.20源码阅读】runtime/string.go
【代码】【Golang1.20源码阅读】runtime/string.go。
golang 字符串 sed 编译器 -
【Golang1.20源码阅读】crypto/sha256.go
【代码】【Golang1.20源码阅读】crypto/sha256.go。
golang 校验和 数据 ide -
go sync.WaitGroup源码分析
go版本 :1.10.3原理实现:信号量信号量是Unix系统提供的一种保护共享资源的机制,用于防止多个线程同时访问某个资源。可
WaitGroup源码 waitgroup源码 go waitgroup源码分析 信号量 Group