Go语言中WaitGroup的实现与使用要点是什么?
2 个回答
勇敢一点吧!我们不会比今年更年轻了。萧薰儿## 作用 `sync.WaitGroup` 是 Go 提供的并发协同原语,用来等待一组并发任务全部完成,适用于多个子任务无先后顺序要求、主流程必须等待它们结束后才能继续的场景。 ### 核心方法 - `Add(delta int)`:增加或减少内部计数器;启动任务前通常调用 `Add(1)`。 - `Done()`:将计数器减 1,等价于 `Add(-1)`;任务结束时调用。 - `Wait()`:阻塞当前 goroutine,直到计数器变为 0。 ### 基本使用方式 1. 在启动 goroutine 前调用 `Add(n)` 注册任务数量。 2. 每个 goroutine 执行任务,并在结束时调用 `Done()`,通常写成 `defer wg.Done()`。 3. 主 goroutine 调用 `Wait()`,等待所有任务完成后再继续。 示例结构: ```go var wg sync.WaitGroup wg.Add(3) for i := 0; i < 3; i++ { go func() { defer wg.Done() // 执行任务 }() } wg.Wait() ``` ## 原理 `WaitGroup` 内部主要由以下字段组成: - `noCopy`:用于配合 `go vet` 检测 `WaitGroup` 是否被复制;复制后可能导致非预期问题。 - `state atomic.Uint64`:使用一个原子变量保存状态: - 高 32 位表示任务计数器,即尚未完成的 goroutine 数量。 - 低 32 位表示等待者数量,即调用 `Wait()` 后被阻塞的 goroutine 数量。 - `sema`:信号量,用于阻塞和唤醒等待中的 goroutine。 执行过程可以概括为: - `Add` 正数会增加任务计数。 - `Done` 会减少任务计数。 - `Wait` 在计数未归零时通过信号量阻塞等待者。 - 当计数减到 0 时,会唤醒所有等待的 goroutine。 ## 使用要点 - 不要复制 `WaitGroup`,应通过指针传递,避免状态被复制造成错误。 - `Add` 必须在启动 goroutine 之前调用,不要在新 goroutine 内部再调用 `Add`,否则可能出现 `Wait` 已提前返回的竞态。 - `Add` 和 `Done` 必须正确配对,确保计数器最终归零,否则 `Wait()` 会永久阻塞。 - 计数器不能变成负数;多调用 `Done` 或传入过小的 `delta` 会触发 panic。 - 复用 `WaitGroup` 时,必须等上一轮所有 `Wait()` 都返回、计数器已经归零后,才能重新开始下一轮。发布于 2026-10-29 10:07 •IP属地 内蒙古举报评论收藏
态度本身也是答案的一部分。古尘## 作用 - WaitGroup 用于等待一组 goroutine 执行完成。 - 适用于主流程必须等待多个并发任务全部结束后才能继续执行的场景。 - 核心方法: - `Add(delta int)`:增加或减少待完成任务计数,通常在启动 goroutine 前调用。 - `Done()`:表示一个任务完成,等价于 `Add(-1)`。 - `Wait()`:阻塞当前 goroutine,直到任务计数变为 0。 基本使用方式: ```go var wg sync.WaitGroup wg.Add(5) for i := 0; i < 5; i++ { go func() { defer wg.Done() // 执行并发任务 }() } wg.Wait() ``` ## 原理 WaitGroup 的底层结构大致如下: ```go type WaitGroup struct { noCopy noCopy state1 [3]uint32 } ``` ### noCopy - `noCopy` 用于标记 WaitGroup 不可复制。 - WaitGroup 应通过指针在多个 goroutine 或函数间传递。 - 如果复制 WaitGroup,不同副本会拥有不同状态,无法正确同步任务完成情况。 ### state1 `state1` 中保存三类信息: - `counter`:当前未完成的任务数量。 - `waiter`:调用 `Wait()` 后正在等待的 goroutine 数量。 - `sema`:用于阻塞和唤醒等待者的信号量。 执行过程如下: 1. 调用 `Add(n)` 时,`counter` 原子增加 `n`。 2. goroutine 执行完成后调用 `Done()`,本质是将 `counter` 减 1。 3. 调用 `Wait()` 时: - 如果 `counter` 已经为 0,则直接返回。 - 否则将 `waiter` 加 1,并等待信号量唤醒。 4. 当最后一个 `Done()` 使 `counter` 变为 0 时,WaitGroup 根据 `waiter` 数量释放信号量,唤醒所有等待的 goroutine。 ### Add 的关键约束 - 任务计数不能变成负数,否则会触发 `panic: sync: negative WaitGroup counter`。 - `Add` 应当在启动 goroutine 之前调用,避免 `Wait` 提前返回。 - 在已有等待者时,不能重新复用处于错误时机的 WaitGroup,否则可能触发 misuse 相关 panic。 - WaitGroup 可以在一轮任务完成后复用,但必须保证上一轮的 `Wait` 已经结束,再开始下一轮计数。发布于 2026-10-29 08:24 •IP属地 陕西举报评论收藏
公众号进入网站体验更佳

