12.3 recover 恢复程序
本篇学习 recover 捕获 panic 的写法,并能在 defer 中拦截 panic、让外层流程继续执行。
recover 恢复程序
概念说明
recover 用于捕获当前 goroutine 中正在发生的 panic。
它必须在 defer 调用的函数中使用,才能拦截 panic 并让程序恢复到可控状态。
恢复 panic 不等于问题消失。
捕获后通常还要记录日志、返回错误或做兜底处理。
语法/规则
recover() 只有在 deferred 函数中直接调用才有效。- 没有发生 panic 时,
recover() 返回 nil。 - recover 只能捕获当前 goroutine 的 panic。
- 捕获 panic 后,当前函数不会回到 panic 发生点继续执行。
- 不要用 recover 隐藏所有错误,否则排查问题会更困难。
recover 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
| package main
import "fmt"
func safeRun() {
// defer 会在 safeRun 即将结束时执行。
// 如果函数里发生了 panic,Go 会先执行 defer,再决定是否继续向外抛出 panic。
defer func() {
// recover 必须在 defer 的函数里直接调用才有效。
// 如果当前正在处理 panic,这里就能拿到 panic 的内容。
if err := recover(); err != nil {
fmt.Println("recovered:", err)
// 一旦 recover 成功,这次 panic 就被拦住了。
// safeRun 不会回到 panic 那一行继续执行,而是直接结束并返回给调用者。
}
}()
fmt.Println("safeRun: start")
// 这里触发 panic。
// 从这一行开始,safeRun 后面的普通代码都不会继续执行。
panic("something wrong")
// 这一行永远不会执行,因为 panic 发生后流程会立刻转去执行 defer。
// fmt.Println("safeRun: end")
}
func main() {
// safeRun 内部已经把 panic recover 掉了,
// 所以 panic 不会继续传到 main。
safeRun()
// 所谓“恢复”,指的是程序没有因为这次 panic 整体崩溃,
// main 还能继续执行后面的代码。
fmt.Println("main continues")
}
|
这里的“恢复”不是回到 panic("something wrong") 那一行后面继续跑,
而是把原本会继续向上传播的 panic 截住,让 safeRun() 正常结束,随后 main() 继续往下执行。
输出结果:
1
2
3
| safeRun: start
recovered: something wrong
main continues
|
常见错误
- 在普通函数调用中直接写
recover(),无法捕获 panic。 - 以为 recover 可以跨 goroutine 捕获 panic,实际每个 goroutine 要自己处理。
- 捕获后什么也不做,导致错误被静默吞掉。
- 认为 recover 后会回到 panic 那一行继续执行,实际不会。