
在Go语言中,当我们需要启动多个Go协程并发执行任务,并且主Go协程需要等待所有这些任务完成后才能继续后续操作时,sync.WaitGroup提供了一种简洁高效的解决方案。它内部维护一个计数器:
为了更好地理解WaitGroup的工作方式,我们来看一个经典的例子:启动多个工作协程执行模拟任务,并等待它们全部完成。
package main
import (
"fmt"
"sync"
"time"
)
// worker 函数模拟一个耗时任务,并在完成后通知 WaitGroup
func worker(id int, wg *sync.WaitGroup) {
// 确保在 worker 协程退出前调用 wg.Done(),无论是正常完成还是发生 panic
defer wg.Done()
fmt.Printf("Worker %d: 任务开始...\n", id)
time.Sleep(time.Duration(id) * time.Second) // 模拟耗时操作
fmt.Printf("Worker %d: 任务完成。\n", id)
}
func main() {
var wg sync.WaitGroup // 声明一个 WaitGroup 实例
numWorkers := 3 // 我们将启动3个工作协程
fmt.Println("主协程: 启动工作协程...")
// 循环启动工作协程
for i := 1; i <= numWorkers; i++ {
wg.Add(1) // 每次启动一个新协程,计数器加1
go worker(i, &wg) // 启动协程,并传递 WaitGroup 的指针
}
fmt.Println("主协程: 等待所有工作协程完成...")
wg.Wait() // 阻塞主协程,直到 WaitGroup 计数器归零
fmt.Println("主协程: 所有工作协程已完成。程序退出。")
}
代码解析:
运行上述代码,您会看到工作协程并发执行,并且主协程会等待所有工作协程完成后才打印最终的退出信息。
值得注意的是,sync.WaitGroup的主要目的是等待一组Go协程的完成,它关注的是同步任务的生命周期。它不提供任何机制来保护共享资源的并发访问。
与此相对,sync.Mutex(互斥锁)是用于保护共享资源,确保在任何给定时刻只有一个Go协程可以访问该资源,从而避免数据竞争。
在某些场景下,两者可能会被混淆。例如,如果一个问题是关于如何防止多个Go协程同时修改一个变量,那么应该使用sync.Mutex(或sync.RWMutex、sync.Atomic等),而不是sync.WaitGroup。WaitGroup无法解决数据竞争问题,它仅仅是用来协调Go协程的启动与结束。
sync.WaitGroup是Go语言中实现Go协程同步的强大工具,特别适用于“扇出-扇入”(fan-out/fan-in)模式,即启动多个并发任务,然后等待所有任务完成的场景。通过熟练掌握Add、Done和Wait三个方法,开发者可以有效地管理并发Go协程的生命周期,确保程序的正确执行流程。理解其与sync.Mutex等其他同步原语的区别至关重要,以在不同的并发问题中选择最合适的工具。
以上就是Go并发编程:深入理解sync.WaitGroup的详细内容,更多请关注php中文网其它相关文章!
编程怎么学习?编程怎么入门?编程在哪学?编程怎么学才快?不用担心,这里为大家提供了编程速学教程(入门课程),有需要的小伙伴保存下载就能学习啦!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号