设计思路
- 定义任务依赖关系:明确哪些goroutine需要等待其他goroutine完成后才能启动。
- 使用WaitGroup:为每个需要等待的阶段创建一个WaitGroup实例。当一个goroutine完成任务时,调用WaitGroup的
Done
方法;需要等待一组goroutine完成的goroutine则调用Wait
方法。
- 启动goroutine:按照依赖关系顺序启动各个goroutine。
示例代码
package main
import (
"fmt"
"sync"
)
func main() {
var wg1, wg2 sync.WaitGroup
// 启动第一组goroutine
wg1.Add(2)
go func() {
defer wg1.Done()
// 模拟任务1
fmt.Println("Task 1 completed")
}()
go func() {
defer wg1.Done()
// 模拟任务2
fmt.Println("Task 2 completed")
}()
// 等待第一组goroutine完成
go func() {
wg1.Wait()
// 启动第二组goroutine
wg2.Add(1)
go func() {
defer wg2.Done()
// 模拟任务3,依赖任务1和任务2完成
fmt.Println("Task 3 completed")
}()
}()
// 等待所有任务完成
wg2.Wait()
fmt.Println("All tasks completed")
}
WaitGroup使用要点
- 初始化:在使用
WaitGroup
之前,需要通过Add
方法设置需要等待的goroutine数量。
- 调用Done:每个完成的goroutine都应在结束前调用
WaitGroup
的Done
方法,标记自己已完成任务。
- 调用Wait:需要等待一组goroutine完成的goroutine应调用
Wait
方法,阻塞直到所有相关goroutine都调用了Done
。
可能遇到的陷阱
- 忘记调用Add:如果没有调用
Add
方法设置正确的等待数量,Wait
方法可能会提前返回或永远阻塞。
- 重复调用Add:在
Wait
方法已经被调用后再次调用Add
,会导致panic
。所以确保Add
只在Wait
之前调用。
- 忘记调用Done:如果有goroutine没有调用
Done
,Wait
方法将永远阻塞,导致程序无法继续执行。
- 使用不当的并发场景:在多个地方对同一个
WaitGroup
进行操作时,要注意并发安全,避免数据竞争。