Go语言同步原语与数据竞争:WaitGroup

在Go语言并发编程中,我们经常需要等待多个 goroutine 执行完毕后再继续下一步操作。Go 提供的 sync.WaitGroup 就是专为这种**"等待一组任务完成"**而设计的同步原语。


一、基本原理

sync.WaitGroup 提供三个主要方法:

方法 说明
Add(n int) 设置等待的 goroutine 数量(加计数)
Done() 每个 goroutine 完成时调用(减计数)
Wait() 阻塞主 goroutine,直到计数归零

它内部使用计数器+条件变量,当所有 goroutine 都调用 Done() 后,Wait() 才会解除阻塞。


二、典型用法

示例:等待 10 个 goroutine 执行完毕

go 复制代码
package main

import (
    "fmt"
    "sync"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done() // 每个 goroutine 完成时调用
    fmt.Printf("Worker %d is working...\n", id)
    // 模拟工作
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 10; i++ {
        wg.Add(1) // 每启动一个 goroutine,加1
        go worker(i, &wg)
    }

    wg.Wait() // 阻塞直到所有 goroutine 完成
    fmt.Println("All workers done.")
}

输出示意:

erlang 复制代码
Worker 1 is working...
Worker 2 is working...
...
All workers done.

三、常见错误及注意事项

1. Add() 必须在 goroutine 启动前调用

错误示例:

scss 复制代码
go func() {
    wg.Add(1) // 此时 goroutine 可能已开始执行,竞态风险
    ...
}()

正确做法:

scss 复制代码
wg.Add(1)
go func() {
    ...
    wg.Done()
}()

2. 不可重复使用已完成的 WaitGroup(没有"重置"功能)

WaitGroup 设计为一次性同步器,不建议重复使用,若确需控制并发次数,可用 sync.Poolsemaphore 替代。


四、结合匿名函数使用

go 复制代码
for i := 0; i < 5; i++ {
    wg.Add(1)
    go func(i int) {
        defer wg.Done()
        fmt.Println("i:", i)
    }(i)
}
wg.Wait()

⚠️ 注意:传参 i 必须显式传入闭包,避免捕获变量陷阱。


五、使用场景

  • • 等待一组任务执行完成(如:并发下载、批量计算)
  • • 控制主函数在 goroutine 完成后再退出
  • • 可搭配 Channel 和 Context 使用,实现更复杂的并发控制模型

六、小结

  • sync.WaitGroup 是 Go 并发编程中最常用的同步工具之一。
  • • 使用 Add/Done/Wait 实现多协程间的同步等待。
  • • 使用时避免竞态和变量捕获问题。

相关推荐
苏三说技术11 小时前
LangChain4j 和 LangGraph4j,哪个更好?
后端
ServBay12 小时前
7 个AI开发中真正用得上的 MCP Server,配合Claude Code食用效果更佳
后端·claude·mcp
妙码生花12 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(十五):优化细节、网络请求封装
前端·后端·ai编程
用户67570498850213 小时前
Go 语言里判断字符串为空,90% 的人都写错了!
后端·go
用户67570498850213 小时前
Go 进阶必修:90% 的人都没用对的“表驱动法”
后端·go
小兔崽子去哪了13 小时前
Java 生成二维码解决方案
java·后端
苍何13 小时前
懂事的 Agent 已经开始自己看屏幕干活了,效率起飞!
后端
掘金码甲哥13 小时前
1分钟买不了吃亏系列: nginx动态域名解析
后端
神奇小汤圆14 小时前
2026大厂Java岗面试记录:八股+场景+项目+AI,一文讲透快速上岸路径(含答案)
后端