panic(运行时恐慌)是一种只会在程序运行时才回抛出来的异常。在panic被抛出之后,如果没有在程序里添加任何保护措施的话,程序就会在打印出panic的详情,终止运行。

  如果一个panic是无意间引发的,其中的值只能由Go语言运行时系统给定,但是当使用painc函数有意引发一个panic时,却可以自行指定其包含的值。

  举个栗子

package mainfunc main() {s1 := []int{0, 1, 2, 3, 4}e5 := s1[5]_ = e5
}

  运行上面的代码,会抛出panic

panic: runtime error: index out of rangegoroutine 1 [running]:   //Id为1的goroutine在此panic被引发时正在运行
main.main()/Users/haolin/GeekTime/Golang_Puzzlers/src/puzzlers/article19/q0/demo47.go:5 +0x3d  //此行代码在其所属源码文件中的行数,以及源码文件的绝对路径, +03d是计数偏移量,用处不大。
exit status 2  //以退出状态码2结束运行,一般状态不为0时表示程序非正常退出

从Painc被引发到程序终止运行的大致过程是怎样的?

  某个函数中的某行代码引发了一个panic后,初始的panic详情会被建立起来,并且该程序的控制器会立即从此行代码转移到调用其所属函数的那行代码上(调用栈中的上一级),此行代码所属函数的执行随即终止。紧接着,控制权并不会在此有片刻停留,它又会立即转移至上一级的调用代码处,反方向传播直至最外层函数(go函数,对于主goroutine来说就是main函数)。但是控制器也不会停留在那里,而是被Go语言运行时系统收回。随后程序奔溃并终止运行,承载程序这次运行的进程也会随之死亡并消失。与此同时,在这个控制器传播过程中,panic详情会积累和完善,并在程序终止之前打印出来。

//main函数调用了caller1函数,caller1函数调用了caller2函数
goroutine 1 [running]:
main.caller2()/Users/haolin/GeekTime/Golang_Puzzlers/src/puzzlers/article19/q1/demo48.go:22 +0x91
main.caller1()/Users/haolin/GeekTime/Golang_Puzzlers/src/puzzlers/article19/q1/demo48.go:15 +0x66
main.main()/Users/haolin/GeekTime/Golang_Puzzlers/src/puzzlers/article19/q1/demo48.go:9 +0x66
exit status 2

怎样让panic包含一个值,应该让她包含什么样的值?

  在调用panic函数,把某个值作为参数传给该函数

  由于panic函数的唯一一个参数时空接口类型的,所以从语法上讲,它可以接受任何类型的值。但最好传入errir类型的错误值,或者其他可以被有效序列化(可以更易读地去表示形式转换)的值。使程序崩溃时,panic包含的那个值字符串表示形式会被打印出来

怎样施加panic的保护措施,避免程序奔溃?

  Go语言的内建函数recover专用于恢复panic。recover函数无需任何参数,并且会返回一个空接口类型的值。如果用法正确,这个值实际上就是即将恢复的panic包含的值,并且如果这个panic是因我们调用panic函数引发的,那么该值同时也会是此次调用panic函数时,传入的参数值副本。

  上面强调用法正确,那什么是不正确的用法?

package mainimport ("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 mainimport ("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 mainimport "fmt"func main() {defer fmt.Println("first defer")for i := 0; i < 3; i++ {defer fmt.Printf("defer in for [%d]\n", i)}defer fmt.Println("last defer")
}//运行结果
last defer
defer in for [2]
defer in for [1]
defer in for [0]
first defer

  如果for语句中包含一条defer语句,那这条defer语句执行次数,就取决于for语句迭代次数。并且同一条defer语句每被执行一次,其中的defer调用就会产生一次,而且这些函数调用同样不会被立即执行。在defer执行时,Go语言会把它携带的defer函数及其参数值另行存储到一个先进后出的队列,相当于一个栈。在需要执行某个函数中defer函数调用时,Go语言会先拿到对应的队列,然后从该队列中一个一个取出defer函数及其参数值,并逐个执行调用

转载于:https://www.cnblogs.com/yuxiaoba/p/9813605.html

【Go】Panic函数相关推荐

  1. golang log.Fatal() 和 panic() 函数的区别

    在讲两者区别之前我们先看一下os.Exit()函数的定义: func Exit(code int)Exit causes the current program to exit with the gi ...

  2. optee中的panic函数实现

    optee3.14中的panic.TEE_Panic.panic.panic实现 ★★★ 个人博客导读首页-点击此处 ★★★ . 说明: 在默认情况下,本文讲述的都是ARMV8-aarch64架构,o ...

  3. panic函数c语言,【go语言学习】错误error和异常panic

    一.错误和异常的区别 错误指的是可能出现问题的地方出现了问题.比如打开一个文件时失败,这种情况在人们的意料之中 . 异常指的是不应该出现问题的地方出现了问题.比如引用了空指针,这种情况在人们的意料之外 ...

  4. go语言panic函数详解

    程序异常被叫做panic,直译为运行时恐慌 当panic被抛出异常后,如果我们没有在程序中添加任何保护措施的话,程序就会打印出panic的详细情况之后,终止运行 panic: runtime erro ...

  5. Go 学习笔记(19)— 函数(05)[如何触发 panic、触发 panic 延迟执行、panic 和 recover 的关系]

    1. 异常设计思想 Go 语言的错误处理思想及设计包含以下特征: 一个可能造成错误的函数,需要返回值中返回一个错误接口( error ),如果调用是成功的,错误接口将返回 nil ,否则返回错误. 在 ...

  6. Go 分布式学习利器(8)-- Go的函数

    文章目录 1. Go语言的函数语法 2. Go 函数中的可变长参数 3. Go函数中的延迟函数 deffer 1. Go语言的函数语法 先介绍一下Go语言的函数和其他语言 函数之间的差异,其中有一些已 ...

  7. golang中的panic

    panic 数组越界,空指针引用等,这些运行时错误会引起panic异常 我们不应该通过调用panic函数来报告普通错误,而应该只把他作为报告致命错误的一种方式. 当某些不应该发生的场景发生时,我们就应 ...

  8. 《Go语言圣经》学习笔记 第五章函数

    <Go语言圣经>学习笔记 第五章 函数 目录 函数声明 递归 多返回值 匿名函数 可变参数 Deferred函数 Panic异常 Recover捕获异常 注:学习<Go语言圣经> ...

  9. go 异常捕获处理 panic defer recover

    简言 在其他语言里,宕机往往以异常的形式存在,底层抛出异常,上层逻辑通过 try/catch 机制捕获异常,没有被捕获的严重异常会导致宕机 go语言追求简洁,优雅,Go语言不支持传统的 try-cat ...

最新文章

  1. 为反对种族歧视,GitHub正在把master/slave等术语替换掉
  2. Future 模式介绍
  3. Gridview实现银行选择列表
  4. java Datasource,数据库连接池
  5. 《剑指offer》c++版本 10. 斐波那契数列
  6. 一个简单问题引发对IEnumerable和IQueryable的思考
  7. Python教程:文件路径/目录获取教程
  8. c# Invoke和BeginInvoke 区别详解
  9. windows 下安装 rabbitmq报init terminating in do_boot错误
  10. servlet配置log4J
  11. 急需能临时发邮件的临时邮箱 临时邮箱怎么注册 邮箱163注册入口在哪
  12. 微信API接口(收藏)
  13. 战旗助手服务器代码,炉石传说酒馆战旗助手
  14. ETL数据处理平台,快速实现数据集成
  15. 2017年苹果开发者账号申请——账号VISA卡支付流程
  16. 猿创征文|智能合约开发效率工具
  17. P5713_洛谷团队系统(深基3.例5)
  18. 杰理强制升级工具4.0使用和原理解析
  19. 计算机网络延展-桥接器(网桥)
  20. C# 在win10/win11调用默认浏览器打开网页

热门文章

  1. php 复制文件夹并压缩到最小_php与语音识别功能如何对接
  2. 获取extjs 选择后的表头_批量写入审计底稿的表头信息设定页面
  3. async和await结合读取文件
  4. 使用ExtractTextPlugin将css和js分开打包
  5. 多个独立的forEach循环, 内部处理条件是一样,代码优化方案
  6. Learning Face Age Progression: A Pyramid Architecture of GANs
  7. Caffe: Vs13添加CUDA支持
  8. linux端口访问规则,linux 查看开发放端口号--iptables
  9. [html]history禁用浏览器的后退功能(包括其他操作后退的按钮,操作等)
  10. 2015北京区域赛 Mysterious Antiques in Sackler Museum 几何基础+思维