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

 更新时间:2023年01月11日 08:31:06   作者:鲲鹏飞九万里  
这篇文章主要介绍了GoLang的panic、recover函数,以及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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • golang DNS服务器的简单实现操作

    golang DNS服务器的简单实现操作

    这篇文章主要介绍了golang DNS服务器的简单实现操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • Golang reflect反射的使用实例

    Golang reflect反射的使用实例

    Golang反射的错误大多数都来自于调用了一个不适合当前类型的方法,而且,这些错误通常是在运行时才会暴露出来,而不是在编译时,如果我们传递的类型在反射代码中没有被覆盖到那么很容易就会panic,本文就介绍一下使用go反射时很大概率会出现的错误,需要的可以参考一下
    2023-04-04
  • golang中sync.Mutex的实现方法

    golang中sync.Mutex的实现方法

    本文主要介绍了golang中sync.Mutex的实现方法,mutex 主要有两个 method: Lock() 和 Unlock(),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-04-04
  • Go命令行参数解析flag 包使用示例详解

    Go命令行参数解析flag 包使用示例详解

    这篇文章主要介绍了Go命令行参数解析flag 包使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2024-01-01
  • Go语言共享内存读写实例分析

    Go语言共享内存读写实例分析

    这篇文章主要介绍了Go语言共享内存读写方法,实例分析了共享内存的原理与读写技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-02-02
  • Go 语言中的 http.FileSystem详细解析

    Go 语言中的 http.FileSystem详细解析

    在本文中,我们深入探讨了 Go 语言中的 http.FileSystem 接口,并介绍了它的基本原理、使用方法以及实际应用场景,感兴趣的朋友跟随小编一起看看吧
    2024-03-03
  • 浅谈Go连接池的设计与实现

    浅谈Go连接池的设计与实现

    本文主要介绍了浅谈Go连接池的设计与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • 从基础到高级全方位解析Go中反射的应用

    从基础到高级全方位解析Go中反射的应用

    本文我们将全面深入地探讨Go语言的反射机制,从反射的基础概念、为什么需要反射,到如何在Go中实现反射,以及在高级编程场景如泛型编程和插件架构中的应用,需要的可以参考下
    2023-10-10
  • 一文解析 Golang sync.Once 用法及原理

    一文解析 Golang sync.Once 用法及原理

    这篇文章主要介绍了一文解析 Golang sync.Once 用法及原理,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-08-08
  • 一文带你搞懂Golang结构体内存布局

    一文带你搞懂Golang结构体内存布局

    结构体在Go语言中是一个很重要的部分,在项目中会经常用到。这篇文章主要带大家看一下结构体在内存中是怎么分布的?通过对内存布局的了解,可以帮助我们写出更优质的代码。感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助
    2022-10-10

最新评论