Go语言并发编程中 WaitGroup 实现原理
Go语言并发编程中 WaitGroup 实现原理
WaitGroup 主要维护了 2 个计数器,一个是请求计数器 v,一个是等待计数 器 w,二者组成一个64bit 的值,请求计数器占高 32bit,等待计数器占低 32bit。
每次 Add 执行,请求计数器 v 加 1,Done 方法执行,等待计数器减 1,v 为0 时通过信号量唤醒 Wait()。
1 回复
Go语言并发编程中 WaitGroup 实现原理
在Go语言的并发编程中,sync.WaitGroup
是一个非常实用的同步原语,用于等待一组协程(goroutines)的完成。它允许主协程阻塞,直到所有的子协程都执行完成。WaitGroup
内部维护一个计数器,每启动一个协程就调用 Add(1)
来增加计数,协程结束时调用 Done()
(实际上是 Add(-1)
)来减少计数。当计数达到零时,所有在 Wait()
上等待的协程都将被唤醒。
实现原理概述
- 内部计数器:
WaitGroup
内部使用一个计数器来跟踪待完成的协程数量。 - 状态存储:
WaitGroup
使用一种高效的方式来存储和修改这个计数器及其状态,以避免在并发环境下的竞态条件。 - 等待队列:当调用
Wait()
时,如果计数器不为零,调用协程会被阻塞并放入一个等待队列中。 - 唤醒机制:每当计数器减到零时,所有在等待队列中的协程都会被唤醒。
示例代码
下面是一个使用 sync.WaitGroup
的简单示例,展示了如何等待多个协程完成:
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 <= 5; i++ {
wg.Add(1) // 增加计数器
go worker(i, &wg) // 启动协程
}
wg.Wait() // 等待所有协程完成
fmt.Println("All workers finished")
}
注意事项
- 使用
WaitGroup
时,务必确保每个Add(1)
调用都有对应的Done()
或Add(-1)
调用,以避免死锁或提前唤醒。 WaitGroup
不能被复制,因此应该通过指针在多个协程间共享。- 在复杂的并发场景中,可能需要结合使用
WaitGroup
与其他同步机制(如互斥锁、条件变量等)来确保程序的正确性和效率。