在Go语言中,sync.WaitGroup
用于等待一组 goroutine 完成。为了避免并发竞争(race condition),需要正确使用 sync.WaitGroup
。以下是一些关键点,帮助你避免并发竞争:
WaitGroup
在开始使用 WaitGroup
之前,确保它被正确初始化。通常,WaitGroup
不需要显式初始化,因为它是一个结构体,默认值就是可用的。
var wg sync.WaitGroup
Add
在启动每个 goroutine 之前,调用 wg.Add(1)
来增加 WaitGroup
的计数器。这样可以确保在 Wait
调用之前,计数器已经正确增加。
wg.Add(1)
go func() {
defer wg.Done()
// 你的代码
}()
Done
在每个 goroutine 结束时,调用 wg.Done()
来减少 WaitGroup
的计数器。通常使用 defer
来确保 Done
一定会被调用。
go func() {
defer wg.Done()
// 你的代码
}()
Wait
在主 goroutine 中调用 wg.Wait()
来等待所有 goroutine 完成。Wait
会阻塞,直到 WaitGroup
的计数器归零。
wg.Wait()
WaitGroup
不要在 goroutine 中修改 WaitGroup
的计数器(即不要在 goroutine 中调用 Add
或 Done
),除非你非常清楚自己在做什么。通常,Add
应该在主 goroutine 中调用,而 Done
应该在每个 goroutine 结束时调用。
sync.WaitGroup
的指针如果你需要在多个函数之间传递 WaitGroup
,建议使用指针传递,以避免复制 WaitGroup
实例。
func worker(wg *sync.WaitGroup) {
defer wg.Done()
// 你的代码
}
func main() {
var wg sync.WaitGroup
wg.Add(1)
go worker(&wg)
wg.Wait()
}
-race
标志检测竞争条件在开发和测试阶段,使用 -race
标志来编译和运行你的程序,以检测潜在的竞争条件。
go run -race main.go
以下是一个完整的示例,展示了如何正确使用 sync.WaitGroup
:
package main
import (
"fmt"
"sync"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Worker %d starting\n", id)
// 模拟工作
fmt.Printf("Worker %d done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
fmt.Println("All workers done")
}
Add
。Done
。Wait
。WaitGroup
的计数器。-race
标志检测竞争条件。通过遵循这些规则,你可以有效地避免并发竞争,并正确使用 sync.WaitGroup
。