errgroup.go()不能捕获panic,因其仅接收func() error函数且不内置recover机制,panic会导致进程崩溃而wait()无法执行;必须在每个go()函数内手动defer/recover将panic转为error返回。

errgroup.Go() 为什么不能直接捕获 panic?
因为 errgroup.Go() 只接收 func() error 类型函数,它不会自动 recover panic —— 一旦 goroutine 内部 panic,整个程序会崩溃,Wait() 根本没机会执行。
常见错误现象:启动多个任务后突然进程退出,日志里只有 panic: xxx,没看到任何 errgroup 的错误返回。
- 必须手动在每个
Go()函数内部加defer/recover,把 panic 转成 error 返回 - 别依赖外部 recover ——
errgroup不接管 goroutine 的 panic 生命周期 - 示例写法:
g.Go(func() error { defer func() { if r := recover(); r != nil { err = fmt.Errorf("panic: %v", r) } }(); return doWork() })
WithContext 启动的 errgroup,goroutine 必须监听 ctx.Done()
很多人以为只要用了 errgroup.WithContext(ctx),goroutine 就能“自动被取消”。其实不是 —— errgroup 只负责调用 cancel(),是否响应、何时退出,全靠你自己在函数里监听 ctx.Done()。
典型坑点:HTTP 请求、数据库查询、time.Sleep() 等阻塞操作不检查上下文,会导致 goroutine 卡住,直到自然结束,白白浪费资源。
- 所有 I/O 操作(
http.Get、db.Query、os.ReadFile)优先使用带ctx的版本(如http.NewRequestWithContext) - 自定义阻塞逻辑必须用
select { case - 别写
time.Sleep(5 * time.Second)这种无上下文感知的代码
SetLimit() 和 TryGo() 配合才能限流,Go() 不受限制
errgroup.Group.SetLimit(n) 本身只是设置一个并发上限值,它不会对 Go() 生效 —— 调用 Go() 依然会立即启动 goroutine,完全无视 limit。
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
真正起作用的是 TryGo():它会在内部检查当前并发数,超出 limit 就直接返回 errors.New("pool exhausted"),不启动新 goroutine。
- 想做并发控制,必须统一用
TryGo(),不能混用Go() -
TryGo()返回 error,需要显式判断:if err := g.TryGo(f); err != nil { /* 处理排队失败 */ } - 注意:limit 是“同时运行中”的 goroutine 数,不是“已提交”总数
Wait() 只返回第一个非 nil 错误,不是全部
这是 errgroup 的设计契约:Wait() 返回的是第一个被设置的非 nil error,后续错误会被忽略。如果你需要汇总所有错误,errgroup 本身不支持。
容易误解的场景:批量健康检查、多 API 并行调用,期望看到“哪几个失败了”,结果只拿到第一个失败项就退出了。
- 真要收集全部错误,得换方案:用
sync.WaitGroup+ 带缓冲的chan error(缓冲大小 ≥ 任务数) -
errgroup的定位是“快速失败 + 协同取消”,不是“错误归档” - 如果业务允许部分失败,且需区分成功/失败项,建议额外维护一个
[]struct{ url string; err error }切片
真正难的不是调用 Go() 和 Wait(),而是让每个 goroutine 主动配合上下文、正确处理 panic、理解 TryGo() 的语义边界 —— 这些地方不写对,errgroup 就只是个看起来高级的 WaitGroup。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










