Golang中的sync.WaitGroup是什么?
参考回答
sync.WaitGroup 是 Go 的 sync 包中提供的一个工具,用于管理和等待一组 goroutine 完成。它通过计数器的机制跟踪 goroutine 的数量,并提供一种简单的方式确保主程序在所有 goroutine 完成之前不会退出。
sync.WaitGroup 的核心方法:
1. Add(delta int):将等待计数器加 delta,通常在启动 goroutine 前调用。
2. Done():将等待计数器减 1,通常在 goroutine 完成时调用。
3. Wait():阻塞主程序,直到计数器归零。
示例代码
package main
import (
"fmt"
"sync"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // goroutine 完成时调用 Done()
fmt.Printf("Worker %d starting\n", id)
// 模拟工作
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
// 启动多个 goroutine
for i := 1; i <= 3; i++ {
wg.Add(1) // 启动一个 goroutine,计数器加 1
go worker(i, &wg)
}
wg.Wait() // 等待所有 goroutine 完成
fmt.Println("All workers finished")
}
输出:
Worker 1 starting
Worker 3 starting
Worker 2 starting
Worker 1 done
Worker 3 done
Worker 2 done
All workers finished
详细讲解与拓展
1. WaitGroup 的工作原理
WaitGroup使用一个内部计数器来记录需要等待的 goroutine 数量。- 调用
Add(n)将计数器加n,表示将等待n个 goroutine。 - 调用
Done()将计数器减1,表示一个 goroutine 完成了。 - 调用
Wait()阻塞当前 goroutine,直到计数器归零,表示所有 goroutine 都完成了。
2. 注意事项
Add必须在启动 goroutine 之前调用:
如果Add在go worker()之后调用,可能会导致计数器为负,导致运行时错误。wg.Add(1) // 正确:在 goroutine 启动前调用 Add go worker(&wg)- 不要重复使用已完成的
WaitGroup:
一个WaitGroup在Wait()返回后,不能再次使用,否则会导致未定义的行为。 -
确保
Done()和Add()配对:
每次Add()的数量必须与Done()的调用次数一致,否则会导致死锁或计数器不匹配。
3. 实际应用场景
-
等待多个 goroutine 完成
适用于需要协调多个 goroutine 并确保所有任务完成后再继续执行的场景。示例:
func main() { var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go func(id int) { defer wg.Done() fmt.Printf("Goroutine %d working\n", id) }(i) } wg.Wait() fmt.Println("All goroutines done") } - 限流和并发控制
配合其他工具(如channel),可以实现对 goroutine 数量的限制。示例:
func main() { var wg sync.WaitGroup semaphore := make(chan struct{}, 3) // 最多允许 3 个 goroutine 并发 for i := 0; i < 10; i++ { wg.Add(1) go func(id int) { defer wg.Done() semaphore <- struct{}{} // 占用一个资源 fmt.Printf("Goroutine %d running\n", id) <-semaphore // 释放资源 }(i) } wg.Wait() fmt.Println("All goroutines done") }
4. sync.WaitGroup 的优势
- 简单易用:通过计数器机制,轻松管理一组 goroutine。
- 线程安全:可以在多个 goroutine 中同时调用其方法。
- 灵活扩展:适用于并发场景的多种需求,例如等待、限流等。
总结
- 核心方法:
Add(delta int):增加等待计数器。Done():减少等待计数器。Wait():阻塞,直到计数器为 0。
- 使用场景:
- 等待一组 goroutine 完成。
- 配合其他工具(如
channel)实现更复杂的并发控制。
- 注意事项:
Add必须在启动 goroutine 之前调用。- 不要重复使用已完成的
WaitGroup。
通过熟练掌握 sync.WaitGroup,可以轻松管理并发任务,提升代码的健壮性和可维护性。