GoLang中panic与recover函数以及defer语句超详细讲解

目录
  • 一、运行时恐慌panic
  • 二、panic被引发到程序终止经历的过程
  • 三、有意引发一个panic并让panic包含一个值
  • 四、施加应对panic的保护措施从而避免程序崩溃
  • 五、多条defer语句多条defer语句的执行顺序

一、运行时恐慌panic

panic是一种在运行时抛出来的异常。比如"index of range"。

panic的详情:

package main
import "fmt"
func main() {
	oneC := []int{1, 2, 3, 4, 5}
	v5 := oneC[5]
	fmt.Println(v5)
}

$ go run demo01.go
panic: runtime error: index out of range [5] with length 5

goroutine 1 [running]:
main.main()
    /Users/lifei/Documents/workspace/githubRepositoies/gowp/projects/go-core-example/src/article19/q1/demo01.go:7 +0x1b
exit status 2
$

打印信息的第一行,"panic: "右边的内容,正是panic包含的runtime.Error类型值的字符串表示形式;

“goroutine 1 [running]” 表示有一个id为 1 的 goroutine在此panic被引发的时候正在运行;

这里的ID编号并不重要,是GO语言运行时系统内部给予的一个goroutine编号,我们在程序中无法获取,也无法改变。

再下面是指出哪一行发生错误。“+0x1b”代表 此行代码相对于其所属函数的入口程序计数偏移量, 一般用途不大。

最后的 “exit status 2”,表明我的这个程序是以退出状态码2结束运行的。

在大多操作系统中,只要退出状态码不是0,都意味着程序运行的非正常结束。

二、panic被引发到程序终止经历的过程

某个函数无疑触发了panic:

  • 初始的panic详情会被建立起来,此行代码所属函数的执行随机终止。
  • 控制权立刻转移到上一级;
  • 控制权如此一层层沿着调用栈的反方向传播至顶端,也就是我们编写的最外层函数;
  • 最终,控制权被GO语言运行时系统收回。随后程序崩溃并终止运行;

panic 详情会在控制权传播的过程中,被逐渐地积累和完善,并且,控制权会一级一级地沿着调用栈的反方向传播至顶端。因此,在针对某个 goroutine 的代码执行信息中,调用栈底端的信息会先出现,然后是上一级调用的信息,以此类推,最后才是此调用栈顶端的信息。

三、有意引发一个panic并让panic包含一个值

  • 可以使用panic函数有意地引发一个 panic。
  • 在调用panic函数时,把某个值作为参数传给该函数就可以了。由于panic函数的唯一一个参数是空接口(也就是interface{})类型的,所以从语法上讲,它可以接受任何类型的值。
  • 但是,我们最好传入error类型的错误值,或者其他的可以被有效序列化的值。这里的“有效序列化”指的是,可以更易读地去表示形式转换。

打印错误信息:

  • 对于fmt包下的各种打印函数来说,error类型值的Error方法与其他类型值的String方法是等价的,它们的唯一结果都是string类型的;
  • 如果某个值有可能会被记到日志里,那么就应该为它关联String方法。

四、施加应对panic的保护措施从而避免程序崩溃

联用defer语句和recover函数调用,才能够恢复一个已经发生的 panic。

GO语言的内建函数recover专门用于恢复panic。recover函数无需任何参数,并且会返回一个空接口类型的值。

defer 语句用来延迟执行代码。延迟到该语句所在的函数即将执行结束的那一刻,无论结束执行的原因是什么。

限制:有一些调用表达式是不能出现在这里的,包括:针对 Go 语言内建函数的调用表达式,以及针对unsafe包中的函数的调用表达式。

package main
import (
	"errors"
	"fmt"
)
func main() {
	fmt.Println("Enter function main")
	// 延迟func函数的执行,直到main结束
	defer func() {
		fmt.Println("Enter defer function")
		if p := recover(); p != nil {
			fmt.Printf("%v\n", p)
		}
		fmt.Println("Exit defer function")
	}()
	// 引发painc
	panic(errors.New("soming wrong"))
	fmt.Println("Exit function main")
}

五、多条defer语句多条defer语句的执行顺序

在同一个函数中,defer函数调用的执行顺序与它们分别所属的defer语句的出现顺序(更严谨地说,是执行顺序)完全相反。

当一个函数即将结束执行时,其中的写在最下边的defer函数调用会最先执行,其次是写在它上边、与它的距离最近的那个defer函数调用,以此类推,最上边的defer函数调用会最后一个执行。

defer语句执行的内幕:

在defer语句每次执行的时候,Go 语言会把它携带的defer函数及其参数值另行存储到一个链表中。

这个链表与该defer语句所属的函数是对应的,并且,它是先进后出(FILO)的,相当于一个栈。

在需要执行某个函数中的defer函数调用的时候,Go 语言会先拿到对应的链表,然后从该链表中一个一个地取出defer函数及其参数值,并逐个执行调用。

package main
import "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")
}

到此这篇关于GoLang中panic与recover函数以及defer语句超详细讲解的文章就介绍到这了,更多相关Go panic recover defer内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Golang 错误捕获Panic与Recover的使用

    目录 一.Golang 错误是什么? 二.错误校验 1.方法 2.判断错误 三.错误捕获 1.方法 2.defer 的使用 总结 一.Golang 错误是什么? 对于Go语言(Golang)的错误是通过返回值的方式,来强迫调用者对错误进行处理,要么你通过 _ 忽略,要么你处理.对于这种设计方式,我们通常需要会写大量的 if err != nil 判断.我们可以通过方法来做到校验. 这类代码非常的多,尽管工程中 error 大部分都是nil,也就是没有任何错误,但是非nil的时候,就意味着错误就出

  • golang 中 recover()的使用方法

    Recover 是一个Go语言的内建函数,可以让进入宕机流程中的 goroutine 恢复过来,recover 仅在延迟函数 defer 中有效,在正常的执行过程中,调用 recover 会返回 nil 并且没有其他任何效果,如果当前的 goroutine 陷入恐慌,调用 recover 可以捕获到 panic 的输入值,并且恢复正常的执行.通常来说,不应该对进入 panic 宕机的程序做任何处理,但有时,需要我们可以从宕机中恢复,至少我们可以在程序崩溃前,做一些操作,举个例子,当 web 服务

  • Golang异常处理之defer,panic,recover的使用详解

    目录 延迟是什么 延迟函数 延迟⽅法 延迟参数 堆栈的推迟 延迟的应⽤ panic和recover(宕机和宕机恢复) panic和recover机制 示例代码 延迟是什么 defer即延迟语句,极个别的情况下,Go才使⽤defer.panic.recover这种异常处理形式. defer可以延迟函数.延迟⽅法.延迟参数. 延迟函数 可以在函数中添加多个defer语句. 当函数执⾏到最后时,这些defer语句会按照逆序执⾏,最后该函数返回.特别是当你在进⾏⼀些打开资源的操作时,遇到错误需要提前返回

  • 小学生也能看懂的Golang异常处理recover panic

    🌌 专注Golang,Python语言,云原生,人工智能领域得博主 💜 过去经历的意义在于引导你,而非定义你, 💜 只要我们足够努力,任何人都有无限潜力 🚀panic 抛出异常函数 🚀recover 捕获异常函数 📣1:在一个主协成内捕获异常 package main import ( "fmt" ) func main(){ defer func(){ err := recover() if err != nil{ fmt.Println("捕获到异常") } }() panic("异常出现") //抛出异常,代表错误代码 } 🚀运行结果 📣2:假设子协成内部错误,看看主协成能不能捕获到 package main import ( "fmt" ) func Calculate(){ panic("异常出现") // 同样代表错误代码 } func main(){ defer func()

  • Golang中panic与recover的区别

    目录 前言 流程 关于recover 前言 与defer类似的是,goroutine 中也有一个_panic链表头指针指向一个_panic链,发生panic的时候也是在链表头插入_panic结构体(执行gopanic) 在执行过程中发生了panic.那么panic以后的代码不会执行,转而执行panic的逻辑,再执行defer,执行到的defer要将started标记为true,同时将其defer结构体中的_panic指针指向当前的_panic,表示这个defer是由该panic触发的.再去执行d

  • golang recover函数使用中的一些坑解析

    目录 正文 一,正常情况下 二, goroutine中panic 三,间接调用recover 四,nil panic 五,总结 正文 众所周知golang 中recover函数可以捕捉panic,防止在出现异常的情况下服务整个不可用.然而某些情况下recover也无法catch panic.下面就会说一些这些情况. 一,正常情况下 package main import "fmt" func main(){     defer func(){         if err := rec

  • GoLang中panic与recover函数以及defer语句超详细讲解

    目录 一.运行时恐慌panic 二.panic被引发到程序终止经历的过程 三.有意引发一个panic并让panic包含一个值 四.施加应对panic的保护措施从而避免程序崩溃 五.多条defer语句多条defer语句的执行顺序 一.运行时恐慌panic panic是一种在运行时抛出来的异常.比如"index of range". panic的详情: package main import "fmt" func main() { oneC := []int{1, 2,

  • Golang中panic的异常处理

    目录 前言 如何恢复panic造成的程序崩溃 何时使用panic 前言 Golang中当程序发生致命异常时(比如数组下标越界,注意这里的异常并不是error),Golang程序会panic(运行时恐慌).当程序发生panic时,程序会执行当前栈中的defer 函数列表.然后打印引发panic的具体信息,最后进程退出,本篇文章我们一起探讨Golang中的panic以及如何利用defer 和 recover 来恢复这种致命的异常 分析造成panic堆栈信息 func main() {     f1(

  • go语言的panic和recover函数用法实例

    Golang 有2个内置的函数 panic() 和 recover(),用以报告和捕获运行时发生的程序错误,与 error 不同,panic-recover 一般用在函数内部.一定要注意不要滥用 panic-recover,可能会导致性能问题,我一般只在未知输入和不可靠请求时使用. golang 的错误处理流程:当一个函数在执行过程中出现了异常或遇到 panic(),正常语句就会立即终止,然后执行 defer 语句,再报告异常信息,最后退出 goroutine.如果在 defer 中使用了 re

  • C语言函数超详细讲解下篇

    目录 前言 函数的声明和定义 函数声明 函数定义 举例 简单的求和函数 把加法单独改写成函数 添加函数声明 带头文件和函数声明 静态库(.lib)的生成 静态库文件的使用方法 函数递归 什么是递归? 递归的两个必要条件 练习1 一般方法 递归的方法 练习2 一般方法 递归方法 练习3 一般方法 递归方法 练习4 一般方法 递归方法 递归与迭代 递归隐藏的问题 如何改进 选递归还是迭代 总结 前言 紧接上文,继续学习函数相关内容. 函数的声明和定义 函数声明 告诉编译器有一个函数叫什么,参数是什么

  • C语言函数超详细讲解上篇

    目录 前言 1.函数是什么? 2.C语言中函数的分类 2.1 库函数 2.1.1 如何学会使用库函数 2.1.2 自定义函数 3.函数的参数 3.1 实际参数(实参) 3.2 形式参数(形参) 4.函数的调用 4.1 传值调用 4.2 传址调用 4.3 练习 4.3.1 判断一个数是不是素数 4.3.2 判断一年是不是闰年 4.3.3 二分查找 4.3.4 数值自增增加1 5.函数的嵌套调用和链式访问 5.1 嵌套调用 5.2 链式访问 总结 前言 本文主要学习函数的相关内容. 1.函数是什么?

  • C语言中main函数与命令行参数详细讲解

    目录 一.main 函数的概念 二.main 函数的本质 命令行 三.main 函数的参数 四.main 函数一定是程序执行的第一个函数吗 五.小结 一.main 函数的概念 C语言中 main 函数称之为主函数 一个C程序是从 main 函数开始执行的 二.main 函数的本质 main 函数是操作系统调用的函数 操作系统总是将 main 函数作为应用程序的开始 操作系统将 main 函数的返回值作为程序的退出状态 下面看一下 main 函数的返回值: A.c: #include <stdio

  • C语言超详细讲解函数栈帧的创建和销毁

    目录 1.本节目标 2.相关寄存器 3.相关汇编指令 4.什么是函数栈帧 5.什么是调用堆栈 6.函数栈帧的创建和销毁 (1).main函数栈帧的创建与初始化 (2).main函数的核心代码 (3).Add函数的调用过程 (4).Add函数栈帧的销毁 (5).调用完成 7.对开篇问题的解答 1.本节目标 C语言绝命七连问,你能回答出几个? 局部变量是如何创建的?为什么局部变量不初始化其内容是随机的?有些时候屏幕上输出的"烫烫烫"是怎么来的?函数调用时参数时如何传递的?传参的顺序是怎样的

  • C语言超详细讲解getchar函数的使用

    目录 一.getchar 函数 二.缓冲区 1.什么是缓冲区 2.为什么要存在缓冲区 3.缓冲区的类型 4.缓冲区的刷新 三.getchar 函数的正确使用 1.getchar 的换行问题 2.getchar 与 scanf 的混合使用 一.getchar 函数 从上面的介绍来看,我们要正确使用getchar函数,首先得了解什么是缓冲区. 二.缓冲区 1.什么是缓冲区 缓冲区又称为缓存,它是内存空间的一部分. 也就是说,在内存空间中预留了一定的存储空间,这些存储空间用来缓冲输入或输出的数据,这部

  • C语言超详细讲解字符串函数和内存函数

    目录 字符串函数 长度不受限制的字符串函数 strlen strcpy strcat strcmp 长度受限制的字符串函数介绍 strncpy strncat strncmp 字符串查找以及错误报告 strstr strtok strerror 内存操作函数 memcpy memmove memcmp 字符串函数 长度不受限制的字符串函数 strlen size_t strlen ( const char * str ) 求字符串长度: 字符串以'\0' 作为结束标志,strlen函数返回的是在

随机推荐