[Go] panic 和 recover
通常情况下,函数向其调用方报告错误的方式都是返回一个 error 类型的值。但是,当遇到致命错误的时候,很可能会使程序无法继续运行。这时,上述错误处理方式就太不适合了,Go 推荐通过调用 panic 函数来报告致命错误。
1. panic
为了报告运行期间的致命错误,Go 内建了专用函数 panic,该函数用于停止当前的控制流程并引发一个运行时恐慌。它可以接受一个任意类型的参数值,不过这个参数值的类型常常会是 string 或者 error,因为这样更容易描述运行时恐慌的详细信息。请看下面的例子:
func main() {
outerFunc()
}
func outerFunc() {
innerFunc()
}
func innerFunc() {
panic(errors.New("An intended fatal error!"))
}
当调用 innerFunc 函数中的 panic 函数后,innerFunc 的执行会被停止。紧接着,流程控制权会交回调用方 outerFunc 函数。然后,outerFunc 函数的执行也将被停止。运行时恐慌就这样沿着调用栈反方向进行传播,直至到达当前 goroutine 的调用栈的最顶层。一旦达到顶层,就意味着该 goroutine 调用栈中所有函数的执行都已经被停止了,程序已经崩溃。
当然,运行时恐慌并不都是通过调用 panic 函数的方式引发的,也可以由 Go 的运行时系统来引发。例如:
myIndex := 4
ia := [3]int{1, 2, 3}
_ = ia[myIndex]
这个示例中的第 3 行代码会引发一个运行时恐慌,因为它造成了一个数组访问越界的运行时错误。这个运行时恐慌就是由 Go 的运行时系统报告的。它相当于我们显示地调用 panic 函数并传入一个 runtime.Error 类型的参数值。顺便说一句,runtime.Error 是一个接口类型,并且内嵌了 Go 内置的 error 接口类型。
显然,我们都不希望程序崩溃。那么,怎样“拦截”一个运行时恐慌呢?
2. recover
运行时恐慌一旦被引发,就会向调用方传播直至程序崩溃。Go 提供了专用于“拦截”运行时恐慌的内建函数 recover,它可以使当前的程序从恐慌状态中恢复并重新获得流程控制权。recover 函数被调用后,会返回一个 interface{} 类型的结果。如果当时的程序正处于运行时恐慌的状态,那么这个结果就会是 非 nil 的。
recover 函数应该与 defer 语句配合起来使用,例如:
defer func() {
if p := recover(); p != nil {
fmt.Printf("Recovered panic: %s\n", p)
}
}()
把此类代码放在函数体的开始处,这样可以有效防止该函数及其下层调用中的代码引发运行时恐慌。一旦发现 recover 函数的调用结果是 非 nil,就应该采取相应的措施。
值得一提的是,Go 标准库中有一种常见的用法值得我们参考。请看标准库代码包 fmt 中的 Token 函数的部分声明:
func (s *ss) Token(skipSpace bool, f func(rune) bool) (tok []byte, err error) {
defer func() {
if e := recover(); e != nil {
if se, ok := e.(scanError); ok {
err = se.err
} else {
panic(e)
}
}
}()
// 省略部分代码
}
在 Token 函数包含的 延迟函数 中,当运行时恐慌携带值的类型是 fmt.scanError 时,这个值就会被赋值给代表结果值的变量 err,否则运行时恐慌就会被重新引发。如果这个重新引发的运行时恐慌传递到了调用栈的最顶层,那么标准输出上就会打印类似这样的内容:
panic: <运行时恐慌被首次引发时携带的值的字符串形式> [recovered]
panic: <运行时恐慌被重新引发时携带的值的字符串形式>
goroutine 1 [running]:
main.func.001()
<调用栈信息> goroutine 2 [runnable:]
exit status 2
这里展现的惯用法有 2 个,如下:
- 可以把运行时恐慌的携带值转换为 error 类型值,并当作常规结果返回给调用方。这样既阻止了恐慌的扩散,又传递了引起恐慌的原因。
- 检查运行时恐慌携带值的类型,并根据类型做不同的后续动作,这样可以精确地控制程序的错误处理行为。
摘自:《Go 并发编程实战(第二版) . 郝林》
[Go] panic 和 recover的更多相关文章
- 【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 ...
- panic和recover的使用规则
转自个人博客 chinazt.cc 在上一节中,我们介绍了defer的使用. 这一节中,我们温习一下panic和recover的使用规则. 在golang当中不存在tye ... catch 异常处理 ...
- 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 ...
- GOLANG错误处理最佳方案errors wrap, Defer, Panic, and Recover
Simple error handling primitives: https://github.com/pkg/errors Defer, Panic, and Recover: ...
- 理解Defer、Panic和Recover
刚开始的时候理解如何使用Defer和Recover有一点怪异,尤其是使用了try/catch块的时候.有一种模式可以在Go中实现和try/catch语句块一样的效果.不过之前你需要先领会Defer.P ...
随机推荐
- v140平台工具集与v110工具集选择
今天在编译用vs2012编译C++动态库提示:error MSB8020: The builds tools for v140_xp (Platform Toolset = 'v140_xp') ca ...
- CF989C A Mist of Florescence (构造)
CF989C A Mist of Florescence solution: 作为一道构造题,这题确实十分符合构造的一些通性----(我们需要找到一些规律,然后无脑循环).个人认为这题规律很巧妙也很典 ...
- 04 uni-app框架学习:禁用顶部原生导航栏
1.在pages.json中配置 比如要首页禁用 就在首页这个选项里 加上这几句代码 2.效果如下
- Javascript - Vue - 指令
指令 v-cloak 解决闪烁,闪烁是指在网速较慢的情况下可能会出现插值表达式{{}}还没有填充数据时会把该表达式直接显示在页面上,如果不希望看到插值表达式则可以使用v-cloak指令,具体做法如下 ...
- Linux下利用backtrace追踪函数调用堆栈以及定位段错误【转】
转自:https://www.linuxidc.com/Linux/2012-11/73470p2.htm 通常情况系,程序发生段错误时系统会发送SIGSEGV信号给程序,缺省处理是退出函数.我们可以 ...
- make distclean
清空bin目录make dirclean 清空所有相关的东西,包括下载的软件包,配置文件,feeds内容等make distclean 这个命令会删除feeds目录及其下面的所有的文件,直接结果就是运 ...
- sonarLint--强大的代码审查工具(插件)
idea也有的一个插件 贴上一个eclipse的sonarlint用法 http://blog.csdn.net/limm33/article/details/51166840 不过听说从2015年1 ...
- Statement执行静态SQL语句
package com.isoftstone.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java. ...
- Using NHibernate with SQLite
The most convenient method to add NHibernate and SQLite for C# project is using NuGet. You can check ...
- 微信隐藏的webJS Api汇总
1.右侧菜单增加"查看公众账号" API document.getElementById('post-user').addEventListener('click', functi ...