recover必须在defer函数体内调用且defer需在panic前注册;它仅捕获当前goroutine正在传播的panic;recover后不可继续业务逻辑,仅用于清理或降级;panic非error替代品,仅适用于真正不可恢复的致命错误。

recover 必须写在 defer 函数体内,且 defer 要在 panic 前注册
直接调用 recover() 永远返回 nil;recover() 只在 panic 正在传播、当前 goroutine 尚未退出时有效。它必须出现在 defer 匿名函数内部,且该 defer 语句必须在 panic() 调用之前执行。
常见错误写法:
-
panic("x"); defer func() { recover() }()—— defer 还没注册,panic 已触发并开始展开栈 -
func f() { r := recover(); panic("x") }——recover()不在 defer 中,根本不会执行
正确结构是:
func f() {
defer func() {
if r := recover(); r != nil {
log.Println("caught:", r)
}
}()
panic("x")
}
goroutine 内 panic 无法被外层 recover 捕获
每个 goroutine 有独立的 panic/recover 作用域。recover() 只能捕获**当前 goroutine 正在传播中的 panic**,对其他 goroutine 里的 panic 完全无效。主线程里写 defer/recover,对 go func() { panic("x") }() 不起作用。
典型误用场景:
- HTTP handler 中启动 goroutine 处理耗时任务,里面 panic → handler 的 recover 完全无感,日志静默丢失
- 用
sync.WaitGroup启动多个 worker,某个 panic 后wg.Done()没执行 →wg.Wait()永远阻塞
必须每个 goroutine 自己加 recover:
go func() {
defer func() {
if r := recover(); r != nil {
log.Printf("worker panic: %v", r)
}
}()
doWork()
}()
recover 后不能继续业务逻辑,只适合清理或降级
recover() 成功后,程序不会回到 panic 那一行重试,而是从 defer 函数返回后继续执行后续语句。此时局部状态大概率已损坏:数组可能部分越界写入、文件句柄可能已失效、channel 发送可能已卡住。
错误做法:
- 在 recover 分支里继续调用依赖原状态的 DB 写入、HTTP 请求、map 修改等操作
- 试图“恢复”并继续处理同一批数据,而不做显式状态校验
合理做法:
- 记录 panic 堆栈 + request ID 或 trace ID
- 返回 fallback 响应或发送告警信号
- 显式
return或通过channel 通知上游,避免状态污染
panic 不是 error 替代品,仅用于真正不可恢复的错误
把 os.Open 失败、HTTP 超时、数据库查不到记录这些可预期、可重试、可降级的常规错误用 panic 处理,会导致测试难、监控漏报、线上行为不可控。
应该用 error 显式返回并由调用方判断:
- 配置加载失败 → 返回
fmt.Errorf("missing config key %q", k),而不是panic - 第三方 API 返回 404 → 记录 warn 日志 + 返回
nil, err,不是 panic - 类型断言失败(
v, ok := x.(T))→ 用ok判断,而非强制断言后 panic
真正适合 panic 的场景极少:如初始化阶段发现监听端口被占用、核心依赖模块未注册、内存分配失败等无法继续运行的致命缺陷。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











