跳转到主内容
极星编程网:以代码为星,赴技术山海!

如何在Golang中使用defer和recover_Golang异常处理与defer语法

defer 在函数返回前按后进先出顺序执行,参数在 defer 语句出现时求值;recover 必须在 defer 函数中直接调用且仅对同 goroutine 中未结束的 panic 有效。 defer 语句的执行时机和栈顺序 defer 不是“异常时才执行”,而是函数返回前(包括正常 return、panic 中断、甚至函数末尾隐式返回)按后进先出(LIFO)顺序执行。很多人误以为 defer 只用于清理资源,其实它更核心的作用是“确定性地绑定执行时机”。 多个
defer
语句会压入栈,最后定义的最先执行,比如:
defer fmt.Println("a")
和
defer fmt.Println("b")
会输出
b
再
a
defer 表达式中的参数在 defer 语句出现时就求值(不是执行时),所以
var i = 0; defer fmt.Println(i); i = 1
输出的是
0
如果想捕获变量的“执行时值”,需用匿名函数包裹:
defer func(v int) { fmt.Println(v) }(i)
recover 必须在 defer 函数中直接调用才有效
recover()
只有在 panic 正在发生、且当前 goroutine 的 defer 链正在执行时才有意义;一旦 panic 结束或离开 defer 函数体,
recover()
就返回 nil,起不到作用。 以下写法无效:
defer recover()
—— 因为
recover()
是函数调用,不是函数值,且参数求值发生在 defer 注册时,此时 panic 还没发生 正确写法必须是:
defer func() { if r := recover(); r != nil { /* 处理 */ } }()
recover 对应的 panic 必须发生在同一 goroutine;跨 goroutine 的 panic 无法被另一个 goroutine 的 defer/recover 捕获 defer + recover 不能捕获所有“错误”,只对 panic 有效 Golang 没有传统 try/catch 异常机制,
error
值是普通返回值,必须显式检查;
recover
完全不介入
error
流程。混淆这两者是常见误区。
panic("network timeout")
可被
recover
拦截;
return errors.New("timeout")
则完全无关 标准库 中绝大多数函数(如
json.Unmarshal
、
os.Open
)都返回
error
,不是 panic,不该也不需要 recover 滥用 recover 试图兜底 error,会导致逻辑混乱、错误被静默吞掉、堆栈丢失 实际场景中 defer/recover 的合理用法 真正适合 recover 的场景极少:通常是顶层 goroutine(如 HTTP handler、goroutine 入口)防止 panic 导致整个服务崩溃,并记录日志;其他地方应优先用 error 返回 + 显式判断。 php版微信js-sdk支付接口类 php版微信js-sdk支付接口类 下载 立即学习 “ go语言免费学习笔记(深入) ”; HTTP handler 示例:
defer func() { if r := recover(); r != nil { log.Printf("panic: %v", r) } }()
,避免单个请求 panic 杀死整个 server 不建议在工具函数里加 recover,比如封装一个
ParseJSON
函数,它该返回
error
,而不是内部 recover 后返回零值 defer 本身开销极小,但大量 defer(如循环内)可能影响性能;不过比起 panic/recover 的代价,defer 本身几乎可忽略 实际写代码时,最易被忽略的是:recover 的 作用域 严格受限于 defer 函数体 + 同一 goroutine + panic 尚未结束。一旦跳出这个三角约束,recover 就彻底失效——这不是 bug,是设计使然。

相关文章