errgroup.withcontext是唯一安全初始化方式,因直接&errgroup.group{}未初始化sync.mutex必panic;必须传入带取消能力的ctx,子任务共享同一取消信号,gocontext用于i/o任务并检查ctx.err(),go用于极快cpu计算,需显式返回error、处理panic、避免循环变量捕获错误。

errgroup.WithContext 是唯一安全的初始化方式
直接 &errgroup.Group{} 或 new(errgroup.Group) 会 panic,因为内部 sync.Mutex 没初始化,调用 Go() 或 Wait() 时崩溃。这不是偶发问题,而是必然失败。
必须用 errgroup.WithContext(ctx),且传入的 ctx 得自带取消能力:
-
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)—— 超时后自动触发取消 -
defer cancel()—— 防止资源泄漏 - 复用已取消的
ctx(比如ctx.Err() != nil)再调WithContext会立即 panic:context canceled
返回的第二个 ctx 是派生上下文,所有子任务共享同一取消信号,这是 errgroup 实现“任一失败即中断其余”的基础。
Go() 和 GoContext() 必须按场景严格区分
混用会导致部分 goroutine “失联”:比如对 HTTP 请求用 Go() 却没手动检查 ctx.Err(),超时后仍卡在 http.Client.Do 上。
- I/O 类任务(HTTP、DB、RPC)一律用
GoContext(func(ctx context.Context) error { ... }),并优先使用支持 context 的 API,例如req.WithContext(ctx)或http.NewRequestWithContext - CPU 密集型纯计算(如 JSON 解析、base64 编码)可用
Go(func() error { ... }),但前提是执行极快;否则必须改用GoContext并定期检查if ctx.Err() != nil { return ctx.Err() } - 别在
GoContext闭包里再调context.WithTimeout—— 会切断上级取消链路
Wait() 只返回第一个错误,且不捕获 panic
g.Wait() 返回 nil 不代表成功,只说明“没遇到非 nil error”,但可能有 goroutine panic 后整个程序已挂掉,根本走不到 Wait()。
- 所有
Go()或GoContext()闭包必须显式returnerror,不能只log.Printf然后return nil,否则错误被静默吞掉 - 易 panic 场景(如解析不可信 JSON、调第三方 SDK)必须加
defer func() { if r := recover(); r != nil { ... } }(),把 panic 转为 error 返回 - 循环变量捕获常见错误:
for _, url := range urls { g.Go(func() error { return fetch(url) }) }—— 所有 goroutine 共享最后一个url值,必须写成url := url再闭包
errgroup 不限流、不汇总错误、不强制中断 goroutine
它只提供取消信号传播机制,是否响应、何时退出、如何清理,全靠业务代码自己写。
- HTTP 请求卡住不是 errgroup 的问题,而是
http.Transport连接池太小(如MaxIdleConns默认 100),大量请求阻塞在roundTrip—— 这时候要调大连接池,不是换 errgroup - 想收集全部错误?得自己配
sync.Mutex+[]error切片,Wait()本身不做这事 - 超时后
Wait()返回context.DeadlineExceeded,但这个错误可能被更早发生的业务错误覆盖(比如第一个完成的 goroutine 就返回了ErrNotFound),依赖错误类型做逻辑分支时要小心
最常被忽略的一点:errgroup 不是“自动 kill goroutine”的黑盒,它只发取消信号;goroutine 是否及时退出,取决于你有没有在阻塞调用前监听 ctx.Done(),或者在循环中检查 ctx.Err()。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











