在Go语言中,sync.WaitGroup
用于等待一组 goroutine 完成。它通过一个计数器来跟踪 goroutine 的数量。Add
方法增加计数器,Done
方法减少计数器,而 Wait
方法会阻塞,直到计数器归零。
为了避免 Wait
方法提前结束,你需要确保以下几点:
Add
和 Done
Add
方法:在启动 goroutine 之前调用 Add
方法,增加计数器的值。这个值应该与你要启动的 goroutine 数量一致。Done
方法:在每个 goroutine 的末尾调用 Done
方法,减少计数器的值。Add
和 Done
的调用次数匹配Add(1)
,那么必须确保在某个地方调用 Done()
,否则 Wait
方法会一直阻塞。Add(n)
,那么必须确保 Done()
被调用 n
次。Add
Add
,因为这可能导致 Wait
方法在 Add
之前被调用,从而导致 Wait
提前结束。defer
调用 Done
defer
调用 Done
,以确保即使 goroutine 发生 panic,Done
也会被调用。package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // 确保 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) // 增加计数器
go worker(i, &wg)
}
wg.Wait() // 等待所有 goroutine 完成
fmt.Println("All workers done")
}
main
函数中,我们启动了 3 个 goroutine,每个 goroutine 都会调用 worker
函数。wg.Add(1)
来增加计数器。worker
函数中,我们使用 defer wg.Done()
来确保在函数结束时减少计数器。wg.Wait()
会阻塞,直到所有 goroutine 都调用了 Done
,即计数器归零。Add
:如果忘记调用 Add
,计数器不会增加,Wait
会立即返回。Done
:如果忘记调用 Done
,计数器不会减少,Wait
会一直阻塞。Add
:这可能导致 Wait
在 Add
之前被调用,从而导致 Wait
提前结束。通过遵循上述规则,你可以确保 Wait
方法不会提前结束,并且能够正确等待所有 goroutine 完成。