sync.WaitGroup
详解
sync.WaitGroup
是 Go 标准库 sync
包提供的一种并发控制机制,用于等待一组 Goroutine 完成执行。它通过计数器管理协程的同步,适用于 “主 Goroutine 等待多个子 Goroutine 全部结束” 的场景。
1. 核心方法
方法 | 作用 |
---|---|
Add(int) |
增加等待的 Goroutine 数量(计数器 +n)。 |
Done() |
标记一个 Goroutine 完成(计数器 -1)。 |
Wait() |
阻塞主 Goroutine,直到计数器归零。 |
2. 基本使用示例
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // Goroutine结束时调用Done()
fmt.Printf("Worker %d starting\n", id)
time.Sleep(time.Second) // 模拟耗时任务
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1) // 每启动一个Goroutine,计数器+1
go worker(i, &wg)
}
wg.Wait() // 阻塞直到所有Goroutine完成
fmt.Println("All workers completed")
}
输出:
Worker 1 starting
Worker 2 starting
Worker 3 starting
Worker 1 done
Worker 2 done
Worker 3 done
All workers completed
3. Key Points
(1) Add()
vs Done()
Add(n)
通常在启动 Goroutine 前调用,表示新增n
个待完成任务。Done()
等价于Add(-1)
,应在每个 Goroutine 退出时调用(通常用defer
确保执行)。
(2) Wait()
- 阻塞当前 Goroutine,直到计数器归零。
- 必须与
Add/Done
配对使用,否则可能死锁或提前退出。
(3) WaitGroup的值传递问题
WaitGroup为结构体类型,需通过指针传递以避免复制:
// ❌错误:值传递会导致死锁!
go worker(i, wg)
// ✅正确:传递指针
go worker(i, &wg)
4. Common Pitfalls (常见陷阱)
(1) Add在Goroutine内部调用
// ❌错误:可能导致主线程先执行Wait()
go func() {
wg.Add(1)
defer wg.Done()
}()
wg.Wait()
修正:
// ✅正确:Add在外部调用
wg.Add(1)
go func() {
defer wg.Done()
}()
wg.Wait()
(2) Done未调用导致死锁
// ❌错误:少调用了Done(),Wait会永久阻塞!
wg.Add(2)
go func() { /*...*/ }() // Missing Done()
wg.Wait()
(3) Add传入负数导致panic
// ❌错误:计数器不能为负!
wg.Add(-5) // panic: negative WaitGroup counter!
5. Advanced Usage (高级用法)
(1) Dynamic Task Addition (动态添加任务)
可在运行时调整计数:
var wg sync.WaitGroup
for _, task := range tasks {
if needsParallel(task) {
wg.Add(1)
go processTask(task, &wg)
}
}
wg.Wait()
(2) Combined with Channels (与Channel结合)
实现结果收集:
results := make(chan int)
var wg sync.WaitGroup
for i :=0; i <10; i++{
wg.Add(1)
go func(x int){
defer wg.Done()
results <- x*x
}(i)
}
//关闭channel避免泄漏(所有goroutines完成后)
go func(){
wg.wait()
close(results)
}()
for res:=range results{
fmt.Println(res)
}
6. Performance Considerations (性能考量)
- WaitGroup基于原子操作实现,轻量高效。
- Avoid频繁创建/销毁(可复用对象)。
7. Alternatives (替代方案)
场景 | 推荐工具 |
---|---|
需要超时控制 | context.Context + Waitgroup |
一对多通知 | close(channel) |
复杂依赖关系 | errgroup.Group |
总结来说:
✅ Use for simple “wait-for-all-goroutines” scenarios.
🚫 Avoid misuse by ensuring every Add has a matching Done!
内容由零声教学AI助手提供,问题来源于学员提问