recover在goroutine中仅对同goroutine内panic有效;批量任务需为每个子goroutine单独设置defer+recover,记录上下文并回传错误。

recover 在 goroutine 中根本不起作用
Go 的 recover 只能在 defer 函数中、且必须在 panic 发生的**同一 goroutine** 内调用才有效。批量任务通常用多个 goroutine 并发执行,如果某个子任务 panic,而你在主 goroutine 调用 recover,完全捕获不到——它早就飞走了。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 每个并发子任务(比如循环里起的 goroutine)都必须包裹自己的
defer+recover - 不要试图在主 goroutine 的
defer里 recover 子 goroutine 的 panic - 若用
sync.WaitGroup等待,panic 后该 goroutine 会直接退出,WG 计数仍会减,但错误被吞掉——必须显式处理
标准 recover 模板要带 error 日志和任务上下文
光 recover() 返回 interface{} 没用,不记录、不分类、不传递,等于没防。尤其批量任务里,你得知道是哪个任务、哪条数据、在哪行崩的。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 在 defer 里用
recover()判断是否非 nil,再转成error(常用fmt.Errorf("%v", r)) - 把任务标识(如
taskID、item.ID)打到日志里,别只写 “panic occurred” - 考虑把 recover 到的 error 通过 channel 或结构体字段回传,供主流程统计失败数或重试
示例片段:
go func(item Data, taskID string) {
defer func() {
if r := recover(); r != nil {
err := fmt.Errorf("task %s panic on item %v: %v", taskID, item.ID, r)
log.Error(err)
results <- Result{Item: item, Err: err}
}
}()
process(item) // 可能 panic
}(item, "import-20240520")recover 不是万能兜底,该校验还得提前校验
靠 recover 挽救逻辑错误(比如空指针、越界、类型断言失败)属于事后补救,掩盖了本该在入口就拦截的问题。批量任务一旦量大,panic 频发会拖慢整体吞吐,还可能掩盖真正 bug。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 对输入参数做前置校验:检查
nil、长度、范围、JSON 解析是否成功等 - 用
if err != nil显式处理已知错误路径,而不是等它们触发 panic - 慎用
panic—— 它该用于“程序无法继续”的严重异常(如配置加载失败),而非业务错误(如用户上传文件格式不对)
使用 errgroup 或 worker pool 时 recover 的位置很关键
用 errgroup.Group 或自建 goroutine 池时,容易误以为在 eg.Go(...) 外包一层 defer 就行。其实 eg.Go 内部新开 goroutine,你的外层 defer 还是在原 goroutine。
实操建议:
立即学习“go语言免费学习笔记(深入)”;
- 在
eg.Go传入的函数体内加defer recover,不是在外面 - worker pool 中,每个从 channel 拿任务并执行的 worker 函数,都要有自己的 recover
- 注意
errgroup默认只返回第一个 panic 错误;如需全部错误,得自己聚合resultschannel
recover 本身不耗资源,但滥用会模糊错误边界;真正难的是判断哪些 panic 应该 recover,哪些该让程序 crash 并修复——尤其是涉及状态不一致(如 DB 写了一半)时,强行 recover 可能比中断更危险。


















