panic和recover的使用规则
转自个人博客 chinazt.cc
在上一节中,我们介绍了defer的使用。 这一节中,我们温习一下panic和recover的使用规则。
在golang当中不存在tye ... catch 异常处理逻辑。在golang当中使用defer, panic和recover来控制程序执行流程,借此来达到处理异常的目的。
Panic是一个可以停止程序执行流程的内置函数。 假设当前F函数当中某处代码触发panic函数,则F函数停止后面代码的执行,转而执行F函数内部的defer函数(如果已经声明了defer函数的话...),然后结束F函数,将当前处理权转给F的调用函数。
对于F的调用方M来说,F是调用panic函数结束的,而不是执行return结束的。这一点很重要,因为调用panic函数结束是没有返回值的。
对于F的调用方M来说,F调用结束并且已经退出。所以引出第一个规则:
调用panic后,调用方函数执行从当前调用点退出
我们通过下面的代码来解释这个问题:
package main
import "fmt"
func main() {
f()
fmt.Println("Returned normally from f.")
}
func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
}
func g(i int) {
if i > 3 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v", i))
}
defer fmt.Println("Defer in g", i)
fmt.Println("Printing in g", i)
g(i + 1)
}
这段函数里面,当i大于3之后,触发panic函数。 在i小于3之前,一切正常。 引出我们来分析一下执行逻辑。
首先f()里面,没有调用g()函数之前,没有异常。因此会输出"Calling g."。而后开始调用g(),当输入的参数小于3之前,会一直输出"Printing in g",(为什么不输出"Defer in g"请参考上一篇文章 (defer的使用规则))。
当i4时,开始触发panic函数,输出Panicking!。需要记住panic之后,调用方直接从调用点退出。 因为g()里面是迭代调用,因此当i4时触发panic时,本质是g(i+1)这一行触发的panic函数。 因此g()后续的函数不再继续执行,因为存在defer函数了,所以连续输出三个"Defer in g"。
此时g(0)函数执行完毕,f()函数退出。因为f()函数里面存在defer函数,因此会调用defer 输出"Recoverd in f"。
所以最终的输出结果将是:
Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.
聊完执行顺序的问题后,我们来看为什么会输出"Recovered in f 4". 这就是规则二:
规则二 通过panic可以设定返回值
panic存在的意义,不仅可以控制异常处理流程,还可以用来返回异常原因。如果panic不给调用方返回异常原因,那么调用方就无从下手处理问题。 因此在调用panic时,一般来说都是返回一个字符串,用来标示失败原因。例如上面代码中的"XXX值不得大于3"。
panic的返回值,通过recover函数来获取。 recover函数也是一个内置函数,专门用来接收panic函数返回值。当panic函数没有被调用或者没有返回值时,recover返回Nil.
在使用recover函数时,就需要下面的规则三:
规则三 recover函数只有在defer代码块中才会有效果
注意此处的有效果不等于执行。言外之意,recover函数可以在任何地方执行,但只有在defer代码块中执行才能真正达到处理异常的目的。
我们假设,将recover函数移除defer代码块,如下:
func f() int {
fmt.Println("Calling g.")
m := g(0)
r := recover()
if r != nil {
fmt.Println("Recovered in f", r)
}
fmt.Println("Returned normally from g.", m)
return m
}
因此recover函数在外,因此执行结果如下:
Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
panic: 4
goroutine 1 [running]:
[stack trace omitted]
可以看到recover函数并未生效,其实是没有执行,因为根据规则一,当前执行流程会跳转到defer函数中。因此只有将recover函数定义到defer之中才会真正被执行。
那么问题来了,recover函数应该定义在哪一级的defer中。 golang是逐级查找的,最终会查找到main函数。 如果main函数中的defer还没有recover函数,golang这会像上面那样抛出最终的异常信息。
本节内容不多,但个人感觉还都是干货。 毕竟我们不能保证自己的写的代码一定就没有问题,有问题不可怕,可怕的是代码不受控制。所以学好defer,panci和recover,有助于写出健壮的代码。
panic和recover的使用规则的更多相关文章
- GOLANG错误处理最佳方案errors wrap, Defer, Panic, and Recover
Simple error handling primitives: https://github.com/pkg/errors Defer, Panic, and Recover: ...
- Golang 高效实践之defer、panic、recover实践
前言 我们知道Golang处理异常是用error返回的方式,然后调用方根据error的值走不同的处理逻辑.但是,如果程序触发其他的严重异常,比如说数组越界,程序就要直接崩溃.Golang有没有一种异常 ...
- 【Go入门教程3】流程(if、goto、for、switch)和函数(多个返回值、变参、传值与传指针、defer、函数作为值/类型、Panic和Recover、main函数和init函数、import)
这小节我们要介绍Go里面的流程控制以及函数操作. 流程控制 流程控制在编程语言中是最伟大的发明了,因为有了它,你可以通过很简单的流程描述来表达很复杂的逻辑.Go中流程控制分三大类:条件判断,循环控制和 ...
- 【GoLang】panic defer recover 深入理解
唉,只能说C程序员可以接受go的错误设计,相比java来说这个设计真的很差劲! 我认为知乎上说的比较中肯的: 1. The key lesson, however, is that errors ar ...
- go语言中使用defer、panic、recover处理异常
go语言中的异常处理,没有try...catch等,而是使用defer.panic.recover来处理异常. 1.首先,panic 是用来表示非常严重的不可恢复的错误的.在Go语言中这是一个内置函数 ...
- Go基础系列:defer、panic和recover
defer关键字 defer关键字可以让函数或语句延迟到函数语句块的最结尾时,即即将退出函数时执行,即便函数中途报错结束.即便已经panic().即便函数已经return了,也都会执行defer所推迟 ...
- Golang错误处理函数defer、panic、recover、errors.New介绍
在默认情况下,当发生错误(panic)后,程序就会终止运行 如果发生错误后,可以捕获错误,并通知管理人员(邮件或者短信),程序还可以继续运行,这当然无可厚非 errors.New("错误信息 ...
- [日常] Go语言圣经-Panic异常,Recover捕获异常习题
Go语言圣经-Panic异常1.当panic异常发生时,程序会中断运行,并立即执行在该goroutine中被延迟的函数(defer 机制)2.不是所有的panic异常都来自运行时,直接调用内置的pan ...
- Golang异常处理-panic与recover
Golang异常处理-panic与recover 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 在程序设计中,容错是相当重要的一部分工作,在 Go中它是通过错误处理来实现的,err ...
随机推荐
- SQL优化一
1.行列转换: decode(条件,值1,返回值1,值2,返回值2,...值n,返回值n,缺省值); select decode(sign(变量1-变量2),-1,变量1,变量2) from dual ...
- Django集成celery实战小项目
上一篇已经介绍了celery的基本知识,本篇以一个小项目为例,详细说明django框架如何集成celery进行开发. 本系列文章的开发环境: window 7 + python2.7 + pychar ...
- LR11 scan correlation 卡死解决方案
LR11 scan correlation 卡死解决方案 笔者在录制脚本时是勾选Enable correlation during recording的,导致后续每次脚本回放都会弹出scan corr ...
- Linux 安装USB摄像头
sudo apt-get update sudo apt-get install fswebcam sudo apt-get install mplayer sudo apt-get install ...
- Linux 常 用 命 令
一:关机命令 1:shutdown 语 法:shutdown [-efFhknr][-t 秒数][时间][警告信息] 说明:shutdown指令可以关闭所有程序,并依用户的需要,进行重新开机或关机的动 ...
- 开涛spring3(4.2) - 资源 之 4.2 内置Resource实现
4.2 内置Resource实现 4.2.1 ByteArrayResource ByteArrayResource代表byte[]数组资源,对于“getInputStream”操作将返回一个By ...
- Ajax请求汇总(一)
刚开始结束Ajax请求的时候,那真的是迷迷糊糊,昏天暗地,通过学习的深入和翻阅各种资料.求助度娘,总结一下Ajax请求,与大家分享一下,希望能给学习Ajax的同学一些帮助,废话不多手,直接开始~~~ ...
- vue2.0+element+node+webpack搭建的一个简单的后台管理界面
闲聊: 今天是六一儿童节哟,小颖祝大家节日快乐哈哈哈.其实这个demo小颖断断续续做了将近两个礼拜了,心塞的,其实这个也没有多难,主要是小颖有点最近事情有点多,所以就把这个一直拖着,今天好不容易做好了 ...
- 搭建eureka服务
1.pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www ...
- HTML5资源教程
新款CSS3按钮组合 5组可爱CSS3按钮 Leave a reply 之前我分享过一些时尚的CSS3动画按钮,比如CSS3渲染Checkbox实现3D开关切换按钮.纯CSS3 3D按钮 按钮酷似牛奶 ...