- panic 是 go 的内置机制,输出严重错误。如果没有恢复程序会打印堆栈,然后退出
- recover 也是 go 的内置机制,在当前 goroutine 恢复 panic,返回值为 panic 中的引用信息
注意: recover 一定要与 defer 一起使用,不然 recover 只会返回 nil
示例程序:
package main
func A() {
B()
}
func B() {
C()
}
func C() {
panic("报错了")
}
func main() {
A()
}
输出结果:
panic: 报错了
goroutine 1 [running]:
main.C(...)
/code/demo/panic/main.go:12
main.B(...)
/code/demo/panic/main.go:8
main.A(...)
/code/demo/panic/main.go:4
main.main()
/code/demo/panic/main.go:16 +0x25
exit status 2
对应的报错堆栈信息比较清晰 C -> B -> A -> main
最后抛到 main 函数
我们可以在 main 调用 A 函数前使用 defer+recover 对 panic 进行捕获
defer func() {
if r := recover(); r != nil {
fmt.Println("捕获到异常", r)
}
}()
A()
如上述代码所示,这样就能成功捕获错误,输出如下
捕获到异常 报错了
recover 只能用于当前 goroutine,我们来试试在子 goroutine 处 panic,main 函数是否能捕获到
package main
import (
"fmt"
"sync"
)
func doSomething() {
defer wg.Done()
fmt.Println("准备panic")
panic("子goroutine报错了")
}
var wg sync.WaitGroup
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("recover捕获到异常值为:", r)
}
}()
wg.Add(1)
go doSomething()
wg.Wait()
fmt.Println("无法捕获")
}
输出结果:
root@pzj:/code/demo/panic# go run main.go
准备panic
panic: 子goroutine报错了
goroutine 6 [running]:
main.doSomething()
/code/demo/panic/main.go:11 +0x90
created by main.main in goroutine 1
/code/demo/panic/main.go:18 +0x31
exit status 2
可以看到子 goroutine 直接 panic 了,而主 goroutine 捕获是没有用的,查看输出
发现无法捕获没有输出,说明在子 goroutine panic 后直接就退出了,根本就不会回到 main,所以 recover 只能恢复当前 goroutine 的 panic,无法恢复其他 goroutine 的