写在前面的话:
panic是Go语言中,用于终止程序的一种函数,往往用在下面两种情况:1)程序出现了很大的故障,例如不能在提供服务了。2)程序在运行阶段碰到了内存异常的操作,例如空指针的取值,改写只读内存等。
对于panic来说,1)场景往往是主动调用;2)场景则是被动调用,panic一旦产生之后,会将堆栈里面的数据dump出来,这样就方便了开发人员来定位问题。
recover是用来截获panic异常信息的,截获了之后,可以控制程序跳过panic的地方继续执行。
本文笔者主要分享下,调用panic之后都做了哪些操作,以及recover使用之后的效果。
1. panic使用介绍
panic一旦触发之后,会按照下面的顺序来做处理:
1)panic开始的地方启动终止程序操作。
2)调用当前触发panic函数里面的defer函数。
3)返回该函数的调用方,当作异常返回来处理,所以这一步也会调用调用方函数的defer,一直到没有调用方为止。
4)打印panic的信息。
5)打印堆栈跟踪信息,也就是我们看到的函数调用关系。
6)终止程序。
例子:
结果分析:通过输出结果我们可以看出,1)2调用了panic开始了panic的操作, 3出不在执行,故没有打印。2)执行testPanic中的defer函数,输出了"testPanic exit!" 。3) 接着,返回到调用方main函数,当作异常终止,调用main中的defer函数,打印"main exit!",main中的6处代码也被跳过执行。4) 打印panic触发时的错误信息"trigger panic!" 。5)打印panic的调用堆栈信息,这里是从触发panic的点开始的,一层层往上调用。6)退出程序。
2. recover使用介绍
recover是go提供的一个用来截获panic信息,重新获取协程控制的函数。它的使用,有两点需要注意,1)recover只能在defer函数中使用。2)recover的使用必须与触发panic的协程是同一个协程才行。
例子1,同一个协程中:
结果分析:通过调用recover之后的结果来看,panic被1处的recover成功截获,只是3处到代码没有执行而已,对于调用testPanic的main函数后续代码都被执行了,"main end!"信息也被打印出来了。
例子2,panic和recover在不同协程中,不能恢复
结果分析:通过输出结果可以看出,就算我们在4处调用了rcover,但是panic也没有被截获,原因就是panic在子协程中,而recover在主协程中,recover不能跨协程截获panic信息。
3.补充知识:
对于panic显示的函数堆栈调用关系,debug.PrintStack()也可以做到,它就是用来打印函数的堆栈调用关系的, 例子如下所示:
下面是panic和recover两个函数的解释说明:https://golang.org/src/builtin/builtin.go?h=panic#L232