recover必须在defer中调用且仅对当前goroutine有效,只能捕获本协程内未被其他recover处理的panic;需在每个可能panic的goroutine入口处配defer+recover,并显式清理资源,不可替代正常错误处理。

recover 必须在 defer 中调用,且仅对当前 goroutine 有效
recover 不是全局错误兜底机制,它只对**当前正在执行的 goroutine** 中最近一次 panic 生效,而且必须在 defer 函数里调用才有效。如果写在普通函数体中、或在 panic 发生后才启动的 goroutine 里调用,recover() 总是返回 nil。
常见错误是把 recover 放在主 goroutine 的 defer 里,试图捕获子 goroutine 的 panic —— 这完全无效。子 goroutine 崩溃不会传播,主 goroutine 毫无感知。
- 每个可能 panic 的 goroutine 都得自己配一套
defer func() { if r := recover(); r != nil { /* 处理 */ } }() -
recover()只能捕获本 goroutine 内部、且尚未被其他 recover 捕获过的 panic - panic 发生后,该 goroutine 的 defer 链仍会按栈逆序执行,这是唯一能调用
recover()的时机
协程 panic 后不处理会导致 goroutine 泄漏和资源残留
Go 不会自动回收 panic 后的 goroutine,它会直接终止并释放栈内存,但其中持有的资源(如打开的文件、网络连接、未释放的锁)若没在 defer 中清理,就真泄漏了。尤其在长期运行的服务中,反复 spawn panic 的 goroutine 会逐步耗尽系统资源。
典型场景:HTTP handler 启动 goroutine 异步处理请求,中间调用第三方库触发 panic,又没做 recover —— 这个 goroutine 就静默消失,但 http.Client 连接可能卡在半开状态,数据库连接池也可能被占满。
立即学习“go语言免费学习笔记(深入)”;
- 务必在 goroutine 入口处加
defer+recover(),哪怕只是打日志 - 在 recover 分支里显式关闭资源:
file.Close()、conn.Close()、mu.Unlock() - 避免在 recover 后继续使用已处于不确定状态的对象(比如 panic 是因指针为空导致的,recover 后再解引用仍会 crash)
recover 捕获到的值类型需断言,不能直接当字符串打印
recover() 返回 interface{},实际类型取决于 panic 时传入的参数。常见的是 string 或 *runtime.Error,但也可能是自定义 error、int、甚至 struct。直接用 fmt.Println(r) 虽能输出,但无法结构化处理或区分错误类型。
更危险的是:某些 panic 是由 runtime 抛出的(如 index out of range),其底层类型不是 error 接口,而是未导出的内部类型,用 err.(error) 断言会 panic。
- 先用
fmt.Sprintf("%v", r)获取可读描述,这是最稳妥的日志方式 - 若需分类处理,用类型 switch:
switch r := recover().(type) { case string: ... case error: ... default: ... } - 不要假设 panic 值一定实现了
Error()方法;runtime panic 的值通常没有
不要用 recover 替代正常错误处理逻辑
recover 是兜底手段,不是错误处理主干。把本该用 if err != nil 判断的 I/O 错误、参数校验失败、业务规则冲突等,统统用 panic + recover 来“优雅”处理,会让代码难以调试、堆栈丢失、性能下降(panic 开销远高于普通分支)。
真正适合 panic 的场景极少:程序无法继续运行的致命状态(如配置加载失败且无默认值)、断言失败(assert 类逻辑)、或极少数必须中断执行流的底层操作(如模板解析器遇到非法语法)。
- HTTP handler 中,
json.Unmarshal失败应返回 400,而不是 panic - 数据库查询返回
sql.ErrNoRows是正常流程,不是 panic 理由 - recover 分支里不要再抛新 panic,否则上层无法捕获;建议统一记录、返回、或调用
os.Exit(1)终止进程
recover 本身不难写,难的是判断哪里该加、加了之后怎么清理、以及怎么避免把它当成万能胶水去粘合所有错误路径。最常被忽略的,是 panic 后资源是否真正释放干净 —— 日志里看到 “recovered panic”,不代表问题真的解决了。


















