当前位置: 技术文章>> 100道Go语言面试题之-Go语言中的sync.WaitGroup是如何实现等待一组goroutine完成的?
文章标题:100道Go语言面试题之-Go语言中的sync.WaitGroup是如何实现等待一组goroutine完成的?
在Go语言中,`sync.WaitGroup` 是一个非常有用的同步原语,用于等待一组 goroutine 的完成。它内部使用计数器来跟踪活动的 goroutine 数量。每个 goroutine 在开始执行时调用 `Add` 方法来增加计数器的值,并在完成执行时调用 `Done` 方法来减少计数器的值。当计数器的值变为 0 时,所有在 `WaitGroup` 上等待的 goroutine(通过调用 `Wait` 方法)都会被唤醒并继续执行。
### 实现机制
1. **计数器(Counter)**:`sync.WaitGroup` 内部维护一个计数器,用于跟踪需要等待的 goroutine 数量。
2. **Add 方法**:`Add(delta int)` 方法用于增加或减少计数器的值。当 delta 为正数时,增加计数器的值;当 delta 为负数时,如果减少后的值小于 0,则会引发 panic。这确保了 `WaitGroup` 不会被错误地减少到负数。
3. **Done 方法**:`Done()` 方法是 `Add(-1)` 的便捷封装,用于在 goroutine 完成时减少计数器的值。
4. **Wait 方法**:`Wait()` 方法会阻塞调用它的 goroutine,直到计数器的值变为 0。这意呀着所有通过 `Add` 方法增加的 goroutine 都已经通过调用 `Done` 方法完成了它们的任务。
### 使用示例
```go
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` 方法返回,程序继续执行。