Go语言中使用sync.WaitGroup等待后台Goroutine执行完成
在Go语言并发编程中,经常需要主流程等待若干个后台执行的Goroutine任务完成后,再进行后续处理或退出程序。本文介绍如何使用标准库中的sync.WaitGroup结构体来实现这一需求,确保资源释放和状态汇总的准确性。
结果与目标
使用sync.WaitGroup可以实现多个异步任务(Goroutine)的同步等待。当所有注册的计数器归零时,主线程或当前函数将解除阻塞,继续执行后续逻辑或正常结束程序。
准备条件
- 环境准备:已安装Go 1.x版本并配置好开发环境。
- 依赖引入:无需额外第三方库,直接使用Go标准库中的
"sync"包。 - 逻辑设计:明确需要等待的任务数量,以便初始化计数器。
第1步:声明变量并设置计数器
首先需要创建sync.WaitGroup类型的变量,并使用Add()方法设置初始计数值。该值通常等于即将启动的Goroutine数量。
var wg sync.WaitGroup
wg.Add(2) // 设置需要等待的任务数量为2
重要参数:count为整数,代表后续调用Done()的次数。如果未正确设置,可能导致死锁或panic。
第2步:启动后台Goroutine并标记完成
启动Goroutine执行具体任务。在任务执行的最后(或在defer语句中),必须调用Done()方法,这会使计数器减1。
// 启动第一个协程
go func() {
defer wg.Done() // 确保无论是否发生错误,计数器都会减1
// do something
fmt.Println("Task 1 completed")
}()
// 启动第二个协程
go func() {
defer wg.Done()
// do something
fmt.Println("Task 2 completed")
}()
建议:推荐使用defer wg.Done()写法,这样可以防止因Goroutine内部提前返回或报错导致Done()未被调用,从而引发死锁。
第3步:主流程阻塞等待
在所有Goroutine启动后,主流程调用Wait()方法。该方法会阻塞直到计数器变为0。
wg.Wait()
fmt.Println("All tasks finished. Exiting.")
可观察状态:控制台输出前两个任务的日志后,才会打印最终的退出提示,证明主线程已成功等待所有子任务完成。
完整实例分析
以下是完整的代码结构,涵盖了从声明到等待的全过程:
package main
import (
"fmt"
"sync"
)
func main() {
// 1. 初始化 WaitGroup
var wg sync.WaitGroup
wg.Add(2) // 预期执行2个任务
// 2. 启动后台任务
go func() {
defer wg.Done()
fmt.Println("Running Task 1...")
// 模拟工作
}()
go func() {
defer wg.Done()
fmt.Println("Running Task 2...")
// 模拟工作
}()
// 3. 等待所有任务完成
wg.Wait()
fmt.Println("All goroutines have exited.")
}
常见排错与维护
- 死锁现象:如果
Wait()被调用但某些Goroutine未调用Done(),程序将永久挂起。请检查是否有路径遗漏了Done()调用。 - Panic异常:如果
Add()的计数器小于0,或者在Wait()之后再次调用Add()且计数器不为0,可能会触发panic。请确保生命周期管理严格遵循顺序。
简要总结
掌握sync.WaitGroup的使用是编写健壮Go并发程序的基础。通过合理设置初始计数和使用defer保障完成标记,可以有效避免竞态条件和资源泄漏。以上就是Go语言使用sync.WaitGroup等待Goroutine完成的详细内容,更多关于Go并发编程的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













