errgroup.wait 默认短路:任一 goroutine 返回错误即刻返回该错误,其余任务不会自动取消;需显式检查 ctx.err() 或用 withcontext 实现取消传播与错误汇总。

errgroup.Wait 会阻塞直到所有 goroutine 完成或首个错误返回
这是 errgroup.Group 最关键的行为特征:它默认是“短路”的。只要任意一个 goroutine 调用 g.Go 启动的任务返回非 nil 错误,g.Wait() 就立刻返回该错误,其余还在跑的 goroutine 不会自动取消——除非你显式用了带 context 的版本。
常见错误现象:
• 任务 A 报错后,任务 B、C 仍在后台运行(可能造成资源泄漏或重复写入)
• 误以为 Wait() 总是等全部完成,结果日志里漏掉 B/C 的错误输出
- 若需「全部执行完再汇总错误」,必须用
errgroup.WithContext(context.Background())+ 手动收集error切片,不能依赖Wait()的返回值 - 若需「任一失败就中止其他」,直接用默认
errgroup.Group{}即可,但记得在g.Go函数里检查是否已被取消(通过传入的ctx.Err()) -
g.Wait()返回的错误是第一个非nil错误,不包含其他 goroutine 的错误信息
errgroup.Go 里的函数必须返回 error,且不能忽略 ctx.Done()
g.Go 接收的函数签名固定为 func() error,它内部会把 panic 捕获转为 error,但不会帮你处理 context 取消逻辑——这点极易被忽略。
使用场景:
• 并发调用多个 HTTP 接口
• 并发读多个文件或数据库查询
• 需要统一超时或取消控制
- 必须在函数开头检查
ctx.Err() != nil,否则即使主流程已超时,goroutine 还在傻跑 - 不要在
g.Go里启动子 goroutine 并直接 returnnil,这会让Wait()认为任务已完成,实际工作却还在后台进行 - 示例中常见写法错误:
go doWork(); return nil—— 应改为return doWork()或显式 select + ctx
并发数量没限制?默认是无限制的 goroutine 泛滥风险
errgroup.Group 本身不做并发控制,g.Go 每次调用都新建 goroutine,如果循环启动几百个,很容易打爆调度器或耗尽内存。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
性能影响:
• 大量 goroutine 切换开销上升
• GC 压力变大(每个 goroutine 栈默认 2KB)
• 网络连接数/文件句柄可能超限
- 需要限流时,别自己手写 channel 控制,直接用
semaphore.NewWeighted(n)包一层 - 更推荐组合用法:
errgroup.WithContext(ctx)+ 外层加信号量,或改用worker pool模式 - 注意:标准库
errgroup没有内置限速 API,别指望Go方法能接受第 2 个参数来限流
error 类型丢失堆栈?errgroup 会吞掉原始 panic 信息
当 g.Go 内部函数 panic,errgroup 会用 fmt.Errorf("panic: %v", r) 包装,原始堆栈就没了——这对调试线上问题很致命。
兼容性影响:
• 无法用 errors.Is/As 判断原始错误类型
• 日志里只看到 "panic: xxx",找不到在哪一行 panic
- 临时方案:在
g.Go函数里手动 recover,并用debug.PrintStack()或runtime/debug.Stack()记录完整堆栈 - 长期建议:避免在
g.Go函数里写可能 panic 的逻辑;或封装一个带堆栈捕获的safeGo辅助函数 - 别依赖
errgroup自动做错误增强,它只负责聚合,不负责诊断
真正难的不是怎么调用 errgroup.Go,而是想清楚:这个并发任务到底要不要取消传播、要不要限流、错误发生后其他任务还值不值得继续——这些决策一旦定错,代码看起来能跑,出问题时却很难定位。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










