Golang的Panic和recover()

229 阅读2分钟

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