Golang WaitGroup原理
最近在学习Golang的WaitGroup,但对它的实现原理还不太理解。想请教下:
- WaitGroup内部是怎么实现计数和等待机制的?
 - 它的Add()、Done()和Wait()方法具体是怎么协作的?
 - 底层使用了哪些同步原语?
 - 在高并发场景下使用WaitGroup需要注意什么? 希望能结合实际代码示例讲解下原理,谢谢!
 
        
          2 回复
        
      
      
        Golang WaitGroup基于计数器实现并发控制。通过Add()增加计数,Done()减少计数,Wait()阻塞直到计数器归零。内部使用信号量机制实现高效等待。适用于等待一组goroutine完成执行的场景。
更多关于Golang WaitGroup原理的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 中的 WaitGroup 是 sync 包提供的一种同步原语,用于等待一组 goroutine 完成执行。其核心原理如下:
核心结构
type WaitGroup struct {
    noCopy noCopy
    state1 [3]uint32
}
noCopy防止值拷贝(编译期检查)。state1存储状态和信号量,包含:- 计数器(counter):当前未完成的 goroutine 数量。
 - 等待器数量(waiter):调用 
Wait()的 goroutine 数。 - 信号量(sema):用于阻塞和唤醒 
Wait()。 
关键方法
- 
Add(delta int)
- 增加或减少计数器(delta 可为负值)。
 - 若计数器变为 0,唤醒所有等待的 goroutine。
 - 若计数器为负,触发 panic。
 
 - 
Done()
- 调用 
Add(-1),表示一个 goroutine 完成。 
 - 调用 
 - 
Wait()
- 等待计数器归零。
 - 若计数器不为 0,阻塞当前 goroutine 直至被唤醒。
 
 
工作流程
- 主 goroutine 调用 
Add(n)设置需等待的 goroutine 数量。 - 启动多个 goroutine,每个在结束时调用 
Done()。 - 主 goroutine 调用 
Wait()阻塞,直到所有 goroutine 完成(计数器为 0)。 
底层实现
- 使用原子操作(
atomic)保证计数器与等待器的线程安全。 - 通过信号量(
sema)实现 goroutine 的阻塞与唤醒:Wait()在计数器 >0 时,通过runtime.semacquire阻塞。- 最后一个 
Done()(计数器归零)通过runtime.semrelease唤醒所有等待者。 
 
示例代码
package main
import (
    "fmt"
    "sync"
)
func main() {
    var wg sync.WaitGroup
    for i := 0; i < 3; i++ {
        wg.Add(1) // 增加计数器
        go func(id int) {
            defer wg.Done() // 完成后减少计数器
            fmt.Printf("Goroutine %d done\n", id)
        }(i)
    }
    wg.Wait() // 等待所有完成
    fmt.Println("All goroutines finished")
}
注意事项
- 调用 
Add()必须在启动 goroutine 前,避免竞态条件。 - 严禁复制 
WaitGroup(通过noCopy保障)。 - 计数器归零后,
WaitGroup可重复使用(通过Add()重置)。 
通过原子操作与信号量的高效结合,WaitGroup 以最小开销实现多 goroutine 的同步等待。
        
      
                    
                    
                    
