1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > go语言错误和异常处理 panic defer recover的执行顺序

go语言错误和异常处理 panic defer recover的执行顺序

时间:2022-02-13 16:39:15

相关推荐

go语言错误和异常处理 panic defer recover的执行顺序

一、panic()和recover()

Golang中引入两个内置函数panic和recover来触发和终止异常处理流程,同时引入关键字defer来延迟执行defer后面的函数。一直等到包含defer语句的函数执行完毕时,延迟函数(defer后的函数)才会被执行,而不管包含defer语句的函数是通过return的正常结束,还是由于panic导致的异常结束。你可以在一个函数中执行多条defer语句,它们的执行顺序与声明顺序相反。当程序运行时,如果遇到引用空指针、下标越界或显式调用panic函数等情况,则先触发panic函数的执行,然后调用延迟函数。调用者继续传递panic,因此该过程一直在调用栈中重复发生:函数停止执行,调用延迟执行函数等。如果一路在延迟函数中没有recover函数的调用,则会到达该协程的起点,该协程结束,然后终止其他所有协程,包括主协程(类似于C语言中的主线程,该协程ID为1)。

panic:1、内建函数2、假如函数F中书写了panic语句,会终止其后要执行的代码,在panic所在函数F内如果存在要执行的defer函数列表,按照defer的逆序执行3、返回函数F的调用者G,在G中,调用函数F语句之后的代码不会执行,假如函数G中存在要执行的defer函数列表,按照defer的逆序执行,这里的defer有点类似try-catch-finally中的finally4、直到goroutine整个退出,并报告错误

recover:1、内建函数2、用来控制一个goroutine的panicking行为,捕获panic,从而影响应用的行为3、一般的调用建议a).在defer函数中,通过recever来终止一个gojroutine的panicking过程,从而恢复正常代码的执行b).可以获取通过panic传递的error

简单来讲:go中可以抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理。

错误和异常从Golang机制上讲,就是error和panic的区别。很多其他语言也一样,比如C++/Java,没有error但有errno,没有panic但有throw。

Golang错误和异常是可以互相转换的:

错误转异常,比如程序逻辑上尝试请求某个URL,最多尝试三次,尝试三次的过程中请求失败是错误,尝试完第三次还不成功的话,失败就被提升为异常了。异常转错误,比如panic触发的异常被recover恢复后,将返回值中error类型的变量进行赋值,以便上层函数继续走错误处理流程。

什么情况下用错误表达,什么情况下用异常表达,就得有一套规则,否则很容易出现一切皆错误或一切皆异常的情况。

以下给出异常处理的作用域(场景):

空指针引用下标越界除数为0不应该出现的分支,比如default输入不应该引起函数错误

其他场景我们使用错误处理,这使得我们的函数接口很精炼。对于异常,我们可以选择在一个合适的上游去recover,并打印堆栈信息,使得部署后的程序不会终止。

说明: Golang错误处理方式一直是很多人诟病的地方,有些人吐槽说一半的代码都是"if err != nil { / 打印 && 错误处理 / }",严重影响正常的处理逻辑。当我们区分错误和异常,根据规则设计函数,就会大大提高可读性和可维护性。

代码演示:

package mainimport "fmt"func main() {/*panic:词义"恐慌",recover:"恢复"go语言利用panic(),recover(),实现程序中的极特殊的异常的处理panic(),让当前的程序进入恐慌,中断程序的执行recover(),让程序恢复,必须在defer函数中执行*/defer func(){if msg := recover();msg != nil{fmt.Println(msg,"程序回复啦。。。")}}()funA()defer myprint("defer main:3.....")funB()defer myprint("defer main:4.....")fmt.Println("main..over。。。。")}func myprint(s string){fmt.Println(s)}func funA(){fmt.Println("我是一个函数funA()....")}func funB(){//外围函数fmt.Println("我是函数funB()...")defer myprint("defer funB():1.....")for i:= 1;i<=10;i++{fmt.Println("i:",i)if i == 5{//让程序中断panic("funB函数,恐慌了")}}//当外围函数的代码中发生了运行恐慌,只有其中所有的已经defer的函数全部都执行完毕后,该运行恐慌才会真正被扩展至调用处。defer myprint("defer funB():2.....")}

运行结果:

我是一个函数funA()....我是函数funB()...i: 1i: 2i: 3i: 4i: 5defer funB():1.....defer main:3.....funB函数,恐慌了 程序回复啦。。。

可见当外围函数的代码中发生了运行恐慌,只有其中所有的已经defer的函数全部都执行完毕后,该运行恐慌才会真正被扩展至调用处。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。