recover仅在同goroutine的defer中调用才有效,用于捕获panic;跨goroutine无效,且恢复后状态可能损坏,应优先预防而非依赖recover。

recover 只在 panic 发生时有效,且必须在 defer 中调用
recover 不是通用的错误捕获机制,它只对当前 goroutine 中由 panic 触发的异常起作用。如果没在 defer 函数里调用,recover 会直接返回 nil,什么也捞不到。
常见错误是把 recover 放在普通函数里、或者放在 defer 外面:
func badExample() {
recover() // 永远返回 nil,毫无意义
defer func() {
// 这里才对,但得确保 panic 确实发生在 defer 执行前
if r := recover(); r != nil {
log.Println("caught:", r)
}
}()
panic("boom")
}
-
recover必须紧挨着defer,且该defer必须在panic之前注册(即在同 goroutine 中先执行defer注册,再触发panic) - 跨 goroutine 的 panic 无法被其他 goroutine 的
recover捕获 —— Go 不支持“全局异常处理器” - 如果
panic后程序已退出当前函数栈(比如 panic 在 goroutine 起始处就发生),而 defer 还没来得及运行,recover就失效了
goroutine 崩溃时 recover 无法阻止程序终止
主 goroutine(main)中未被 recover 的 panic 会导致整个程序退出;其他 goroutine 中未 recover 的 panic 虽不会终止进程,但该 goroutine 会静默死亡,且不会通知其他协程。
这意味着:你不能靠一个全局 recover 来“兜底”所有 goroutine 的错误。
- 每个可能 panic 的 goroutine 都得自己配
defer + recover - 别指望在 main 函数里 defer 一次就能 catch 子 goroutine 的 panic
- 子 goroutine panic 后若没 recover,日志都不打 —— 默认行为是 silent exit,容易误判为“逻辑没执行到”
recover 后继续运行是安全的,但状态可能已损坏
recover 能让 goroutine 从 panic 栈展开中恢复执行,控制权回到 defer 函数之后的代码,这点是安全的。但不等于“一切如常”。
关键问题是:panic 可能发生在任意语句中间,比如刚写了一半结构体字段、刚 unlock 了 mutex、刚 close 了 channel……此时程序状态大概率不一致。
- 不要在 recover 后继续使用可能被中断修改的共享变量(如全局 map、未加锁的计数器)
- 避免在 recover 后重试原操作 —— 很可能重复提交、重复消费、重复写库
- 推荐做法:recover 后只做清理(close channel、unlock、log)、然后 return 或启动新 goroutine 隔离后续逻辑
替代方案比盲目 recover 更可靠
很多场景下,与其依赖 recover,不如提前预防 panic。Go 的并发模型鼓励显式错误处理,而非异常兜底。
- 用
sync.Pool替代频繁 new + panic 风险的切片扩容 - 用
context.Context控制超时和取消,而不是靠 panic 中断流程 - 对不可信输入(如 JSON 解析、类型断言)优先用 ok-idiom:
v, ok := x.(T),而非直接断言后 panic - 第三方库调用前查文档:是否明确说明会 panic?比如
json.Unmarshal(nil, &v)会 panic,应提前判空
真正需要 recover 的地方其实很少:主要是插件系统、HTTP handler 顶层兜底、或封装 Cgo 调用时防止崩溃穿透。其它时候,它更像一把双刃剑 —— 用错比不用还危险。











