panic触发时,当前函数立即停止执行,已注册的defer按后进先出顺序执行;仅限本函数内defer,recover必须在defer中调用才有效,跨goroutine panic无法被外部recover捕获。

panic 发生时 defer 怎么执行?
panic 一触发,当前函数立刻停在那,不再往下走,但已注册的 defer 会按后进先出(LIFO)顺序挨个执行。注意:只执行当前函数内注册的 defer,不是整个调用链上所有 defer 都一起跑。
- 如果
defer里调用了recover(),且刚好捕获到本次panic,那么 panic 链就断了,后续代码继续执行 - 如果
defer里没调recover(),或者recover()调得太晚(比如在另一个 defer 之后),那就没用,panic 继续往上调用栈传 -
defer函数本身如果也 panic,会覆盖前一个 panic(除非它自己 recover),最终只留下最后一个 panic 的值
示例中常见错误是把 recover() 写在普通函数里,而不是 defer 匿名函数中——这时它永远返回 nil。
recover 必须写在 defer 里,但 defer 可以嵌套
recover() 只在 defer 函数体内有效,这是硬性限制。但它不挑 defer 的写法:可以是匿名函数、命名函数、带参数的闭包,只要它被 defer 延迟执行就行。
立即学习“go语言免费学习笔记(深入)”;
- 错误写法:
func bad() { recover() // 这里永远无效 panic("boom") } - 正确写法:
func good() { defer func() { if r := recover(); r != nil { fmt.Println("caught:", r) } }() panic("boom") } - 进阶用法:多个 defer 嵌套时,
recover()只能捕获“本层” panic;若外层 defer 没 recover,内层 recover 就白写了
跨 goroutine 的 panic 无法被外部 recover 捕获
每个 goroutine 是独立的 panic 作用域。go func() { panic("x") }() 触发的 panic,只能由该 goroutine 自己的 defer + recover() 拦住,main goroutine 或其他协程里的 recover() 完全看不到。
- 常见陷阱:在 HTTP handler 里起 goroutine 处理耗时任务,结果 panic 导致协程静默退出,连接不释放、日志没打、监控无响应
- 解决方案:每个可能 panic 的 goroutine 都要自带兜底
defer+recover(),哪怕只是记录日志+退出 - 不要指望全局 recover —— Go 没有这种机制,强行用 channel 或 sync.WaitGroup 传递 panic 值属于绕路,且容易漏处理
panic 不是 error,别拿它当常规错误处理
panic 和 error 是两类东西:error 是值,用于业务流程控制;panic 是运行时状态,代表程序已进入不可恢复境地。
- 数组越界、空指针解引用、map 并发写这些 runtime panic,说明代码有严重缺陷,不该靠 recover 吞掉,而应修复逻辑
- 主动
panic("config missing")适合服务启动阶段校验失败,但一旦上线,这类 panic 应转为log.Fatal或健康检查失败,而非让请求流程去 recover - 频繁触发
panic会带来堆栈展开开销,性能敏感路径(如高频 RPC)里尤其要避免
真正难的不是写对 recover,而是判断“这里到底该 panic 还是该 return error”,以及“这个 panic 到底该不该被 recover”。多数线上事故,都卡在这一步。


















