在Go语言中,sync.WaitGroup
是一个非常有用的同步原语,用于等待一组 goroutine 的完成。它内部使用计数器来跟踪活动的 goroutine 数量。每个 goroutine 在开始执行时调用 Add
方法来增加计数器的值,并在完成执行时调用 Done
方法来减少计数器的值。当计数器的值变为 0 时,所有在 WaitGroup
上等待的 goroutine(通过调用 Wait
方法)都会被唤醒并继续执行。
实现机制
计数器(Counter):
sync.WaitGroup
内部维护一个计数器,用于跟踪需要等待的 goroutine 数量。Add 方法:
Add(delta int)
方法用于增加或减少计数器的值。当 delta 为正数时,增加计数器的值;当 delta 为负数时,如果减少后的值小于 0,则会引发 panic。这确保了WaitGroup
不会被错误地减少到负数。Done 方法:
Done()
方法是Add(-1)
的便捷封装,用于在 goroutine 完成时减少计数器的值。Wait 方法:
Wait()
方法会阻塞调用它的 goroutine,直到计数器的值变为 0。这意呀着所有通过Add
方法增加的 goroutine 都已经通过调用Done
方法完成了它们的任务。
使用示例
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // 确保 goroutine 结束时减少计数器
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 <= 5; i++ {
wg.Add(1) // 增加计数器
go worker(i, &wg)
}
wg.Wait() // 等待所有 worker 完成
fmt.Println("All workers have finished")
}
在这个例子中,我们启动了 5 个 goroutine 来模拟并行工作。每个 goroutine 在开始时通过 wg.Add(1)
增加计数器,并在结束时通过 defer wg.Done()
(即 defer wg.Add(-1)
)减少计数器。main
函数中的 wg.Wait()
调用会阻塞,直到所有 worker goroutine 都通过调用 Done
方法完成了它们的任务,此时计数器的值变为 0,Wait
方法返回,程序继续执行。