recover仅在同goroutine的defer中调用才有效,用于捕获panic;跨goroutine无效,且恢复后状态可能损坏,应优先预防而非依赖recover。
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)r
ecover 捕获 —— Go 不支持“全局异常处理器”panic 后程序已退出当前函数栈(比如 panic 在 goroutine 起始处就发生),而 defer 还没来得及运行,recover 就失效了主 goroutine(main)中未被 recover 的 panic 会导致整个程序退出;其他 goroutine 中未 recover 的 panic 虽不会终止进程,但该 goroutine 会静默死亡,且不会通知其他协程。
这意味着:你不能靠一个全局 recover 来“兜底”所有 goroutine 的错误。
defer + recover
recover 能让 goroutine 从 panic 栈展开中恢复执行,控制权回到 defer 函数之后的代码,这点是安全的。但不等于“一切如常”。
关键问题是:panic 可能发生在任意语句中间,比如刚写了一半结构体字段、刚 unlock 了 mutex、刚 close 了 channel……此时程序状态大概率不一致。
很多场景下,与其依赖 recover,不如提前预防 panic。Go 的并发模型鼓励显式错误处理,而非异常兜底。
sync.Pool 替代频繁 new + panic 风险的切片扩容context.Context 控制超时和取消,而不是靠 panic 中断流程v, ok := x.(T),而非直接断言后 panicjson.Unmarshal(nil, &v) 会 panic,应提前判空真正需要 recover 的地方其实很少:主要是插件系统、HTTP handler 顶层兜底、或封装 Cgo 调用时防止崩溃穿透。其它时候,它更像一把双刃剑 —— 用错比不用还危险。