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 完成。