sync.WaitGroup 是 Go 语言中用于等待一组 goroutine 完成执行的同步工具。Add(1) 是 WaitGroup 的一个方法,用于增加等待的 goroutine 数量。以下是关于 Add(1) 的使用方法和注意事项:
Add(1) 通常在你启动一个新的 goroutine 之前调用,表示你将要等待一个 goroutine 完成。每个 Add(1) 调用都会增加 WaitGroup 的内部计数器。
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 数量
go worker(i, &wg)
}
wg.Wait() // 等待所有 goroutine 完成
fmt.Println("All workers done")
}
Add(1) 的位置:Add(1) 应该在启动 goroutine 之前调用,以确保 WaitGroup 的计数器在 goroutine 开始执行之前就已经增加。
Done() 的调用:每个 Add(1) 都应该对应一个 Done() 调用,通常在 goroutine 的末尾使用 defer wg.Done() 来确保 Done() 一定会被调用。
避免竞态条件:确保 Add(1) 和 Wait() 的调用是在同一个 goroutine 中进行的,以避免竞态条件。
以下是一个错误的示例,展示了 Add(1) 和 Wait() 在不同 goroutine 中调用可能导致的竞态条件:
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.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++ {
go func(i int) {
wg.Add(1) // 错误的 Add(1) 位置
worker(i, &wg)
}(i)
}
wg.Wait() // 可能提前返回,因为 Add(1) 可能在 Wait() 之后执行
fmt.Println("All workers done")
}
在这个错误示例中,Add(1) 在 goroutine 内部调用,可能导致 Wait() 在 Add(1) 之前执行,从而导致 Wait() 提前返回。
Add(1) 应该在启动 goroutine 之前调用。Add(1) 都应该对应一个 Done() 调用。Add(1) 和 Wait() 的调用是在同一个 goroutine 中进行的,以避免竞态条件。通过正确使用 Add(1) 和 Done(),你可以确保 WaitGroup 能够正确地等待所有 goroutine 完成。