
本文深入探讨了 sync.WaitGroup 在 Go 语言中的安全重用问题。通过示例代码和源码分析,阐明了 sync.WaitGroup 在 Wait() 调用后可以安全重用,并且可以并发地从多个 goroutine 中调用 Wait()。只要 Add 操作发生在 Wait 之前,就能保证程序的正确性。
sync.WaitGroup 是 Go 语言中用于等待一组 goroutine 完成的同步原语。它主要有三个方法:Add(delta int) 用于增加等待的 goroutine 数量,Done() 用于标记一个 goroutine 完成,Wait() 用于阻塞直到所有等待的 goroutine 都完成。
一个常见的问题是,在调用 Wait() 之后,是否可以安全地重用 sync.WaitGroup。答案是肯定的,可以安全重用。
sync.WaitGroup 的安全重用
Go 语言的设计允许 sync.WaitGroup 在 Wait() 调用后被安全地重用。这意味着你可以在一个循环中多次使用同一个 sync.WaitGroup,而无需每次都创建一个新的实例。
立即学习“go语言免费学习笔记(深入)”;
以下是一个示例,展示了如何在循环中重用 sync.WaitGroup:
Android开发指南中文pdf版,学习android的朋友可以参考下。应用程序基础Application Fundamentals 关键类 应用程序组件 激活组件:intent 关闭组件 manifest文件 Intent过滤器 Activity和任务 Affinity(吸引力)和新任务 加载模式 清理堆栈 启动任务 进程和线程 进程 线程 远程过程调用 线程安全方法 组件生命周期 Activity生命周期 调用父类 服务生命周期 广播接收器生命周期 进程与生命周期 用户界面User Interface
package main
import (
"fmt"
"sync"
)
func worker(who string, in <-chan int, wg *sync.WaitGroup) {
for i := range in {
fmt.Println(who, i)
wg.Done()
}
}
func main() {
var wg sync.WaitGroup
AIn := make(chan int, 1)
BIn := make(chan int, 1)
go worker("a:", AIn, &wg)
go worker("b:", BIn, &wg)
for i := 0; i < 4; i++ {
wg.Add(2)
AIn <- i
BIn <- i
wg.Wait()
fmt.Println("main:", i)
}
close(AIn)
close(BIn)
}在这个例子中,sync.WaitGroup 在循环的每次迭代中都被重用。每次迭代都会调用 wg.Add(2) 来增加等待的 goroutine 数量,然后启动两个 goroutine,并在它们完成时调用 wg.Done()。最后,wg.Wait() 用于等待这两个 goroutine 完成。
并发调用 Wait()
更进一步,sync.WaitGroup 甚至允许从多个 goroutine 中并发地调用 Wait()。这在某些复杂的并发场景中非常有用。
Add 操作必须在 Wait 之前
一个重要的前提条件是,Add 操作必须在 Wait 操作之前发生。如果 Wait 在 Add 之前被调用,可能会导致程序出现意想不到的行为,例如死锁。
sync.WaitGroup 的内部实现
为了更好地理解 sync.WaitGroup 的工作原理,我们可以简单了解一下它的内部实现。在 Go 语言的源码中,sync.WaitGroup 结构体包含一个互斥锁(Mutex)、两个 int32 类型的计数器(counter 和 waiters)以及一个信号量(sema)。
type WaitGroup struct {
m sync.Mutex
counter int32
waiters int32
sema *uint32
}- m:互斥锁,用于保护 counter 和 waiters 的并发访问。
- counter:计数器,表示需要等待的 goroutine 数量。
- waiters:等待者的数量,即有多少个 goroutine 在等待 Wait() 返回。
- sema:信号量,用于在计数器变为零时唤醒等待的 goroutine。
注意事项
- 确保 Add 操作的参数为正数,表示要等待的 goroutine 数量。
- 每次调用 Done() 都会减少计数器,当计数器变为零时,所有等待的 goroutine 都会被唤醒。
- 避免在 Add 之后立即调用 Wait,这可能会导致程序死锁。
总结
sync.WaitGroup 是 Go 语言中一个强大而灵活的同步原语。它可以安全地重用,并且允许并发地调用 Wait()。只要确保 Add 操作发生在 Wait 之前,就可以放心地使用 sync.WaitGroup 来管理并发 goroutine 的执行。理解 sync.WaitGroup 的工作原理和注意事项,可以帮助你编写更健壮、更可靠的并发程序。









