recover必须在defer函数中调用才有效,直接在普通函数体调用永远返回nil;它仅能捕获同goroutine中已注册的defer所处上下文内的panic,无法跨goroutine生效,且panic后栈已展开,recover仅用于终止panic并执行清理。

recover 必须在 defer 中调用才有效
直接在普通函数体里写 recover() 永远返回 nil,因为它只能捕获当前 goroutine 中、且由同层 defer 延迟执行时发生的 panic。Go 的 panic/recover 不是传统 try-catch,而是一次性、仅限于 defer 上下文的“现场抢救”。
-
recover()只在 defer 函数中被调用时才可能非 nil - 如果 panic 发生在其他 goroutine,当前 goroutine 的
recover()完全无感知 - defer 语句必须在 panic 触发前已注册(即 panic 前已执行到该 defer 行)
典型错误:recover 放错位置或漏掉 defer
常见误写是把 recover() 写在普通逻辑块里,或者 defer 里没调用它,又或者 defer 写在 panic 之后——这些都导致恢复失败。下面这个例子看似合理,实则无效:
func badExample() {
panic("boom")
defer func() {
if r := recover(); r != nil {
fmt.Println("never reached")
}
}()
}正确写法必须保证 defer 在 panic 前注册,且 recover 在 defer 函数体内调用:
func goodExample() {
defer func() {
if r := recover(); r != nil {
fmt.Printf("recovered: %v\n", r) // 输出: recovered: boom
}
}()
panic("boom")
}recover 不能跨 goroutine 捕获 panic
每个 goroutine 有独立的 panic/recover 作用域。主 goroutine 中的 defer + recover 对子 goroutine 的 panic 完全无效。这是最容易忽略的限制。
立即学习“go语言免费学习笔记(深入)”;
- 子 goroutine panic 后会直接终止,不会传播到父 goroutine
- 若需处理子 goroutine 异常,必须在子 goroutine 内部加 defer + recover
- 配合
sync.WaitGroup或errgroup.Group时,尤其要注意 panic 是否被各自 goroutine 自行 recover
例如以下代码中,main 的 recover 捕不到 goroutine 里的 panic:
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("not triggered")
}
}()
go func() {
panic("in goroutine") // 这个 panic 不会被上面的 recover 捕获
}()
time.Sleep(10 * time.Millisecond)
}recover 后程序继续执行,但栈已展开完毕
recover 成功后,panic 被终止,控制权回到 defer 所在函数,后续代码照常运行——但注意:panic 发生点之后的所有 deferred 函数(尚未执行的)仍会按 LIFO 顺序执行,且 recover 只能调用一次(再次调用返回 nil)。
- recover 不是“回滚”,它不恢复栈帧,也不重试出错语句
- 不要依赖 recover 来“修复”状态;应把它用于资源清理、日志记录或优雅降级
- 若多个 defer 链式调用,recover 只在第一个捕获它的 defer 中生效,后续 defer 里的 recover 返回 nil
真正需要的是明确知道 panic 可能发生在哪里,并在最靠近风险点的位置做隔离和恢复,而不是靠一层大 defer 包住整个 main 函数。










