【Go】Panic函数
panic(运行时恐慌)是一种只会在程序运行时才回抛出来的异常。在panic被抛出之后,如果没有在程序里添加任何保护措施的话,程序就会在打印出panic的详情,终止运行。
如果一个panic是无意间引发的,其中的值只能由Go语言运行时系统给定,但是当使用painc函数有意引发一个panic时,却可以自行指定其包含的值。
举个栗子
package main
func main() {
s1 := []int{, , , , }
e5 := s1[]
_ = e5
}
运行上面的代码,会抛出panic
panic: runtime error: index out of range goroutine [running]: //Id为1的goroutine在此panic被引发时正在运行
main.main()
/Users/haolin/GeekTime/Golang_Puzzlers/src/puzzlers/article19/q0/demo47.go: +0x3d //此行代码在其所属源码文件中的行数,以及源码文件的绝对路径, +03d是计数偏移量,用处不大。
exit status 2 //以退出状态码2结束运行,一般状态不为0时表示程序非正常退出
从Painc被引发到程序终止运行的大致过程是怎样的?
某个函数中的某行代码引发了一个panic后,初始的panic详情会被建立起来,并且该程序的控制器会立即从此行代码转移到调用其所属函数的那行代码上(调用栈中的上一级),此行代码所属函数的执行随即终止。紧接着,控制权并不会在此有片刻停留,它又会立即转移至上一级的调用代码处,反方向传播直至最外层函数(go函数,对于主goroutine来说就是main函数)。但是控制器也不会停留在那里,而是被Go语言运行时系统收回。随后程序奔溃并终止运行,承载程序这次运行的进程也会随之死亡并消失。与此同时,在这个控制器传播过程中,panic详情会积累和完善,并在程序终止之前打印出来。
//main函数调用了caller1函数,caller1函数调用了caller2函数
goroutine [running]:
main.caller2()
/Users/haolin/GeekTime/Golang_Puzzlers/src/puzzlers/article19/q1/demo48.go: +0x91
main.caller1()
/Users/haolin/GeekTime/Golang_Puzzlers/src/puzzlers/article19/q1/demo48.go: +0x66
main.main()
/Users/haolin/GeekTime/Golang_Puzzlers/src/puzzlers/article19/q1/demo48.go: +0x66
exit status
怎样让panic包含一个值,应该让她包含什么样的值?
在调用panic函数,把某个值作为参数传给该函数
由于panic函数的唯一一个参数时空接口类型的,所以从语法上讲,它可以接受任何类型的值。但最好传入errir类型的错误值,或者其他可以被有效序列化(可以更易读地去表示形式转换)的值。使程序崩溃时,panic包含的那个值字符串表示形式会被打印出来
怎样施加panic的保护措施,避免程序奔溃?
Go语言的内建函数recover专用于恢复panic。recover函数无需任何参数,并且会返回一个空接口类型的值。如果用法正确,这个值实际上就是即将恢复的panic包含的值,并且如果这个panic是因我们调用panic函数引发的,那么该值同时也会是此次调用panic函数时,传入的参数值副本。
上面强调用法正确,那什么是不正确的用法?
package main import (
"fmt"
"errors"
) func main() {
fmt.Println("Enter function main.")
// 引发 panic。
panic(errors.New("something wrong"))
p := recover()
fmt.Printf("panic: %s\n", p)
fmt.Println("Exit function main.")
}
在上面这个函数中先通过调用panic函数引发一个panic,紧接着想通过调用recover函数恢复这个panic。但这个recover函数不会起到任何作用,因为panic一旦发生,控制权会沿着调用栈反方向传播,所以在panic函数调用之后的代码,根本没有执行的机会。
那如果把recover函数的代码提前呢?即先调用recover函数,再调用panic函数。这样也不行,因为在调用recover函数时未发生panic,那么该函数就不会做任何事情,只会返回一个nil。
那怎样才是正确的做法呢?
这就要用到defer语句。defer语句是被用来延迟执行代码的。延迟到该语句所在的函数即将执行结束的那一刻,无论结束执行的原因是什么,即使导致它执行结束的原因是一个panic,因此联用defer语句和recover函数调用,能够恢复一个已经发生的panic
package main import (
"fmt"
"errors"
) func main() {
fmt.Println("Enter function main.")
defer func(){
fmt.Println("Enter defer function.")
if p := recover(); p != nil {
fmt.Printf("panic: %s\n", p)
}
fmt.Println("Exit defer function.")
}()
// 引发 panic。
panic(errors.New("something wrong"))
fmt.Println("Exit function main.")
}
尽量把defer语句写在函数体开始处,因为在引发panic的语句之后的所有语句,都不会有任何执行机会。只有这样defer函数中的recover函数调用才会拦截,并恢复defer语句所属的函数,及其调用代码中发生的所有panic
如果一个函数中有多条defer语句,那么那几个defer函数调用的执行顺序是怎样的?
在同一个函数中,defer函数调用的执行顺序和它们所属的defer语句的出现顺序完全相反。当一个函数即将结束执行时,写在最下面的defer函数调用会最先执行,其次是写在它上边的与它距离最近的defer函数调用,以此类推
package main
import "fmt"
func main() {
defer fmt.Println("first defer")
for i := ; i < ; i++ {
defer fmt.Printf("defer in for [%d]\n", i)
}
defer fmt.Println("last defer")
}
//运行结果
last defer
defer in for []
defer in for []
defer in for []
first defer
如果for语句中包含一条defer语句,那这条defer语句执行次数,就取决于for语句迭代次数。并且同一条defer语句每被执行一次,其中的defer调用就会产生一次,而且这些函数调用同样不会被立即执行。在defer执行时,Go语言会把它携带的defer函数及其参数值另行存储到一个先进后出的队列,相当于一个栈。在需要执行某个函数中defer函数调用时,Go语言会先拿到对应的队列,然后从该队列中一个一个取出defer函数及其参数值,并逐个执行调用
【Go】Panic函数的更多相关文章
- go语言之进阶篇显式调用panic函数
1.显式调用panic函数 示例: package main import "fmt" func testa() { fmt.Println("aaaaaaaaaaaaa ...
- panic和recover的使用规则
转自个人博客 chinazt.cc 在上一节中,我们介绍了defer的使用. 这一节中,我们温习一下panic和recover的使用规则. 在golang当中不存在tye ... catch 异常处理 ...
- GO开发[四]:golang函数
函数 1.声明语法:func 函数名 (参数列表) [(返回值列表)] {} 2.golang函数特点: a. 不支持重载,一个包不能有两个名字一样的函数 b. 函数是一等公民,函数也是一种类型,一个 ...
- go语言中使用defer、panic、recover处理异常
go语言中的异常处理,没有try...catch等,而是使用defer.panic.recover来处理异常. 1.首先,panic 是用来表示非常严重的不可恢复的错误的.在Go语言中这是一个内置函数 ...
- Go基础系列:defer、panic和recover
defer关键字 defer关键字可以让函数或语句延迟到函数语句块的最结尾时,即即将退出函数时执行,即便函数中途报错结束.即便已经panic().即便函数已经return了,也都会执行defer所推迟 ...
- Golang 笔记 4 defer、error、panic
一.defer语句 defer语句仅能被放置在函数或方法中.它由关键字defer和一个调用表达式组成.这里的表达式所表示的既不能是对Go语言内建函数的调用也不能是对Go语言标准库代码包unsafe中的 ...
- [日常] Go语言圣经-Panic异常,Recover捕获异常习题
Go语言圣经-Panic异常1.当panic异常发生时,程序会中断运行,并立即执行在该goroutine中被延迟的函数(defer 机制)2.不是所有的panic异常都来自运行时,直接调用内置的pan ...
- [Go] 如何正确地 抛出 错误 和 异常(error/panic/recover)?
序言 错误 和 异常 是两个不同的概念,非常容易混淆.很多程序员习惯将一切非正常情况都看做错误,而不区分错误和异常,即使程序中可能有异常抛出,也将异常及时捕获并转换成错误.从表面上看,一切皆错误的思路 ...
- [Go] panic 和 recover
通常情况下,函数向其调用方报告错误的方式都是返回一个 error 类型的值.但是,当遇到致命错误的时候,很可能会使程序无法继续运行.这时,上述错误处理方式就太不适合了,Go 推荐通过调用 panic ...
随机推荐
- Python2和Python3语法区别
1.使用for循环进行换行 python 2.x, print 不换行>>> print x, python 3.x print 不换行>>> print(x, e ...
- Android商城开发系列(七)—— 使用RecyclerView展示首页数据
前面我们讲到了使用OkHttp请求网络和FastJson解析数据了,接下来我们就开始把获取到的数据通过数据适配器展示在页面上了.Adapter是用来帮助填充数据的中间桥梁,简单点说就是:将各种数据以合 ...
- Android商城开发系列(六)——使用 OkHttpUtils 请求网络 + 使用 fastjson解析数据
OkHttp是Google推荐使用的一个开源的网络请求框架,Android开发中涉及到网络请求和接口调用现在大部分都是使用OkHttp,网上已经有不少人针对OkHttp进行了封装,这里推荐一下鸿洋大神 ...
- 【Python图像特征的音乐序列生成】关于小样本的一些思考
我之前就注意到,深度学习和音乐结合,尤其是从乐理出发进行结合(而不是纯粹的进行音乐生成),是一个尚未被深度挖掘的全新领域.可想而知,这个方向符合我要求的数据肯定是要自己搜集了. 自己搜集的数据,在量上 ...
- CodeForces 77C Beavermuncher-0xFF (树形dp)
不错的树形dp.一个结点能走多次,树形的最大特点是到达后继的路径是唯一的,那个如果一个结点无法往子结点走,那么子结点就不用考虑了. 有的结点不能走完它的子结点,而有的可能走完他的子节点以后还会剩下一些 ...
- Processing分形之一——Wallpaper
之前用C语言实现过一些分形,但是代码比较复杂.而对于天生对绘图友好的Processing,及其方便. 在大自然中分形普遍存在,我们用图形模拟,主要是找到一个贴近的函数. 代码 /** * Wallpa ...
- Linux centos 6 配置php环境,扩展redis
1.首先安装一个虚拟机(我自己版本:VM 10.0.4) yum -y install openssl psmisc openssl-devel php-devel pcre-devel gcc gc ...
- JDBC连接数据库报错:java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or represents more than one time zone. ......
问题:Java程序使用JDBC连接MySQL数据库时,控制台报错如下: java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' ...
- TCP/IP协议头部结构体
TCP/IP协议头部结构体(转) 网络协议结构体定义 // i386 is little_endian. #ifndef LITTLE_ENDIAN #define LITTLE_ENDIAN (1) ...
- lua拷贝二进制文件的方法
使用lua拷贝二进制文件相比文本文件复杂一点,方法如下 function copyFunc(targetPath,sourcePath) local rf = io.open(sourcePath,& ...