Painc用法是:用于抛出错误【类似与PHP里面的throw new Exception("error!")】
recover
是一个内建函数,用于重新获得 panic 协程的控制,
recover
函数只有在defer
代码块中才会有效果- 在defer函数中,通过recover停止 panic 续发事件(Panicking Sequence),从而恢复正常代码的执行【所以第一个例子为什么可以打印
main end
的原因就是这个】
看个例子:
package main
import "fmt"
func f2() {
fmt.Println("打开数据库连接...")
defer func() {
err := recover()
fmt.Println(err)
fmt.Println("释放数据库连接...")
}()
panic("出现严重错误!")
fmt.Println("run after") //不会打印
}
func main() {
defer fmt.Println("main defer")
fmt.Println("main begin")
f2()
fmt.Println("main end")
}
输出结果:
main begin
打开数据库连接...
出现严重错误!
释放数据库连接...
main end
main defer
当程序运行到panic("出现严重错误!")
的时候会调用延迟函数里面的逻辑,recover()会捕获panic的错误信息。所以先打印fmt.Println(err)
,然后再打印fmt.Println("释放数据库连接...")
,在执行完 recover()
之后,panic 会停止,程序控制返回到调用方(在这里就是 main
函数),程序在发生 panic 之后,从第 fmt.Println("main end")
行开始会继续正常地运行。程序会打印 main end
,之后打印main defer
我们再看一个例子,把上面代码改一下:
package main
import "fmt"
func f2() {
fmt.Println("打开数据库连接...")
defer func() {
fmt.Println("释放数据库连接...")
}()
panic("出现严重错误!")
fmt.Println("run after") //不会打印
}
func main() {
defer fmt.Println("main defer")
fmt.Println("main begin")
f2()
fmt.Println("main end") //不会打印
}
结果是:
main begin
打开数据库连接...
释放数据库连接...
main defer
panic: 出现严重错误!
goroutine 1 [running]:
main.f2()
D:/workspace/go/src/test/main.go:12 +0xbe
main.main()
D:/workspace/go/src/test/main.go:19 +0xf4
exit status 2
这个例子我把recover去掉了,保留了延迟函数defer,
当程序到panic("出现严重错误!")
行发生 panic 时,首先执行了延迟函数打印释放数据库连接...,接着控制返回到函数调用方在这里就是 main
函数),调用方的延迟函数继续运行打印了main defer
,直到到达顶层调用函数。现在程序控制到达了顶层函数,因此该函数会打印出 panic 信息panic: 出现严重错误!
,然后是堆栈跟踪,最后终止程序。
附一张图:
更详细的文章看这篇Go 系列教程 —— 32. panic 和 recover