recover必须在defer函数体内直接调用,且defer必须在panic前注册;每个goroutine需独立加recover;recover后不可继续业务逻辑,仅用于清理、降级或记录。

recover 必须写在 defer 函数体内,且 defer 要在 panic 前注册
很多人写成 panic("x"); defer func() { recover() }(),这根本捕获不到。因为 defer 是在 panic 之后才注册的,而 panic 会立刻展开栈、执行已注册的 defer,但此时这个 defer 还没注册上。
defer 语句必须出现在 panic 之前,才能确保它被纳入当前 goroutine 的 defer 链;recover() 必须是 defer 匿名函数体内的**直接调用**,不能包在另一层函数里(比如 go func() { recover() }())。
错误示例:func f() { panic("boom"); r := recover() } —— recover() 根本不会执行。
正确写法:func f() { defer func() { if r := recover(); r != nil { log.Print(r) } }(); panic("boom") }
每个 goroutine 必须独立加 recover,跨 goroutine 无效
recover 只能捕获当前 goroutine 正在传播中的 panic,主线程里写了 defer + recover,对 go func() { panic("x") }() 完全无效。子 goroutine 崩溃后静默退出,主 goroutine 不知情。
HTTP handler 中起 goroutine 处理耗时任务,里面 panic → handler 不会触发 recover → 日志无声丢失。
解决方案不是在外层加 recover,而是每个 worker 内部自己加:go func() { defer func() { recover() }(); doWork() }()。
封装 safeGo 时,必须确保传入的函数内部也带 recover,否则只是把崩溃从显式变成隐蔽。
recover 后不能继续业务逻辑,只适合清理、降级或记录
recover() 成功后,程序不会回到 panic 那一行重试,而是从 defer 函数返回后继续执行后续语句。此时局部状态大概率已损坏。
- 数组越界后,
arr[x] = 88已失败,但arr可能部分初始化、指针可能已解引用失败 - 不要在
recover分支里调用依赖该状态的业务代码(如再次写 DB、发 HTTP 请求) - 合理做法:记录
panic堆栈 + request ID → 返回 500 或 fallback 响应 → 显式return或os.Exit(1) -
recover()不回滚任何副作用,它不是事务机制,只是提供一次“软着陆”机会
自动重启需封装状态隔离的高阶函数,避免 goroutine 泄漏
简单地在 defer recover() 中直接 go printer() 会引发两个关键问题:
- 递归启动失控:每次
panic都新建 goroutine,旧 goroutine 的循环仍在阻塞等待 channel,导致 goroutine 泄漏 - 状态不可控:
pCount是全局变量,在并发下非线程安全,且无法精准绑定到单个 worker 实例
✅ 正确做法是将 panic 恢复与重启逻辑解耦并封装为高阶函数,确保每个 goroutine 的生命周期独立、状态隔离,并显式控制重启上限:
go recoverer(5, 1, func() { printer(1, c) })
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











