不能直接用 sync.waitgroup 做错误传播,因为它只等待 goroutine 结束而不处理错误;errgroup.group 内置错误传播与自动取消机制,配合 withcontext 和信号量可安全限并发。

为什么不能直接用 sync.WaitGroup 做错误传播
因为 sync.WaitGroup 只管“等所有 goroutine 结束”,不关心它们是否出错。一旦某个任务返回 error,你得自己设共享变量、加锁判断、提前取消其余任务——容易漏处理、竞态难调试。errgroup.Group 就是为解决这个而生:它内置错误传播机制,首次非 nil 错误会自动取消其余 goroutine(如果用了 WithContext),且阻塞在 Wait() 直到全部完成或出错。
如何正确初始化 errgroup.Group 并控制并发数
默认的 errgroup.Group 没有并发限制,所有任务会立刻启动。若要限速(比如防止 HTTP 请求打爆后端),得用带上下文的构造方式,并配合 semaphore 或手动调度。更常用的是:errgroup.WithContext + 外层控制 goroutine 数量:
g, ctx := errgroup.WithContext(context.Background())
sem := make(chan struct{}, 5) // 并发上限 5
<p>for _, task := range tasks {
task := task // 防止闭包引用同一变量
g.Go(func() error {
sem </p><p>注意:<code>sem</code> 必须是带缓冲通道;<code>defer</code> 里的 `</p><h3>errgroup.Wait 返回 error 后还能继续调用吗</h3><p>不能。<code>errgroup.Group</code> 的状态是一次性的:<code>Wait()</code> 返回后,内部状态已终结。再次调用 <code>Wait()</code> 会立即返回上次的错误(或 <code>nil</code>),但不会重新等待。如果想复用,必须新建 <code>errgroup.Group</code> 实例。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/gongju/2525" title="Go语言(Golang)1.26.0"><img
src="https://img.php.cn/upload/manual/001/589/237/6a6adeed24a4a355.png" alt="Go语言(Golang)1.26.0" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/gongju/2525" title="Go语言(Golang)1.26.0" class="overflowclass">Go语言(Golang)1.26.0</a>
<p class="overflowclass">Go语言(Golang)1.26.0版本官方下载,版本号 1.26.0,适合旧项目维护、兼容性测试和指定版本开发环境搭建。</p>
</div>
<a rel="nofollow" href="/xiazai/gongju/2525" title="Go语言(Golang)1.26.0" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
- 常见误用:把同一个
g实例反复用于多个任务批次 - 正确做法:每个任务批次都 new 一个
errgroup.Group - 如果任务之间有依赖,别指望靠重用
g实现“链式执行”——那是context或状态机的事
ctx.Cancel() 被触发时,goroutine 真的会立刻退出吗
不会自动退出,得你自己检查 ctx.Err() 并主动返回。errgroup 只负责传递 cancel 信号,不强制终止 goroutine。比如下面这段代码就危险:
g.Go(func() error {
time.Sleep(10 * time.Second) // 忽略 ctx,超时也不会停
return nil
})
正确写法是周期性检测上下文:
g.Go(func() error {
select {
case <p>所有 I/O 操作(如 <code>http.Client.Do</code>、<code>time.Sleep</code>、<code>chan</code> 收发)都应该接受 <code>ctx</code> 参数或响应 <code>ctx.Done()</code>,否则 <code>errgroup</code> 的取消机制形同虚设。</p><p>实际用的时候,最常被忽略的是:goroutine 内部没做上下文感知,或者复用 <code>errgroup.Group</code> 实例。这两点一出,程序要么卡死,要么错误不传播,查起来特别绕。</p>golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










