WaitGroup工作原理
- 计数器机制:
sync.WaitGroup
内部维护一个计数器。当需要等待一组goroutine
完成时,通过Add
方法将计数器增加指定的值,每个要等待的goroutine
对应一个计数单位。
- 阻塞与释放:主线程或其他等待的
goroutine
通过调用Wait
方法进入阻塞状态,直到计数器归零。每个goroutine
完成任务后,调用Done
方法,该方法会将计数器减1。当计数器减为0时,所有因调用Wait
而阻塞的goroutine
都会被释放,继续执行后续代码。
正确使用示例
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Worker %d started\n", id)
time.Sleep(time.Second)
fmt.Printf("Worker %d finished\n", id)
}
func main() {
var wg sync.WaitGroup
numWorkers := 3
for i := 1; i <= numWorkers; i++ {
wg.Add(1)
go worker(i, &wg)
}
fmt.Println("Waiting for all workers to finish...")
wg.Wait()
fmt.Println("All workers have finished")
}
可能导致goroutine泄漏的原因
- 忘记调用
Done
:如果某个goroutine
没有调用Done
方法,计数器不会归零,导致Wait
永远阻塞,其他goroutine
也无法继续执行,造成泄漏。
Add
和goroutine
启动顺序问题:如果先启动goroutine
,后调用Add
,可能在Add
之前goroutine
就已经完成并调用Done
,导致计数器变为负数,程序可能会崩溃。
Wait
提前返回:如果在所有goroutine
完成之前,由于某些逻辑错误,Wait
提前返回,可能导致部分goroutine
未完成就被遗弃,造成泄漏。