Golang中panic的实现教程

AI 概述
分析造成 panic 堆栈信息如何恢复 panic 造成的程序崩溃何时使用 panic Golang 中当程序发生致命异常时(比如数组下标越界,注意这里的异常并不是 error),Golang 程序会 panic(运行时恐慌)。当程序发生 panic 时,程序会执行当前栈中的 defer 函数列表。然后打印引发 panic 的具体信息,最后进程退...
目录
文章目录隐藏
  1. 分析造成 panic 堆栈信息
  2. 如何恢复 panic 造成的程序崩溃
  3. 何时使用 panic

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

Golang 中 panic 的实现教程

分析造成 panic 堆栈信息

func main() {
    f1()
    fmt.Println("main func end")
}

func f1() {
    fmt.Println("func f1 start")
    arr := []int{}
    fmt.Println(arr[10])
    fmt.Println("func f1 end")
}

上述代码中,我在 main 函数(主协程)中调用了 f1 函数,在调用完该函数后,我打印了「main func end」,程序如果正常执行的话会输出

func f1 start
func f1 end
main func end

很明显我们可以看出 f1 函数中,切片 arr 是没有索引为 10 的元素的,这个时候程序运行时会造成 panic,下面是程序 panic 时,console 打印的堆栈信息

func f1 start
panic: runtime error: index out of range [10] with length 0
goroutine 1 [running]:
main.f1()
/Users/carlos/go/src/test/demo01.go:15 +0x78
main.main()
/Users/carlos/go/src/test/demo01.go:8 +0x20
Process finished with the exit code 2

我们从堆栈中可以发现:

  • 程序会在造成 panic 所处的位置终止,我们可以看到错误信息中只输出了:
    func f1 start
  • 产生 panic 的原因:
    panic: runtime error: index out of range [10] with length 0
  • 是哪里造成的 panic:
    goroutine 1 [running] // 运行该程序的协程
    main.f1()
    /Users/carlos/go/src/test/demo01.go:15 +0x78 // f1 函数,当前 demo01 文件的低 15 行
    main.main()
    /Users/carlos/go/src/test/demo01.go:8 +0x20 // main 函数,当前文件的弟 8 行

从上面的 panic 详情我们可以看出,错误链是通过栈的形式展现出来的(mian 函数先调用,然后在 mian 中调用 f1),所以大家以后在程序发生 panic 时查看堆栈信息时可以先看最上层的错误,因为这里是造成 panic 的根本原因

如何恢复 panic 造成的程序崩溃

Golang 中提供了 recover 函数用来恢复因 panic 造成的程序崩溃。recover 函数有一个返回值来告诉我们 panic 产生的具体原因。下面我们通过代码来进行演示

func main() {
    f1()
    r := recover()
    fmt.Printf("%s \n", r)
    fmt.Println("main func end")
}

func f1() {
    fmt.Println("func f1 start")
    arr := []int{}
    fmt.Println(arr[10])
    fmt.Println("func f1 end")
}

上述代码中我只是在调用 f1 函数的下一行调用了 recover 函数,这样一来我们的理想状态了能够恢复程序,让程序执行完 main 函数中剩下的代码(打印 panic 信息,最后打印 main func end),当我们运行该程序的时候发现 recover 并没有起到作用,这是因为当 f1 造成 panic 时,f1 下方的 recover 函数根本没有机会执行。

下面我将上述代码进行一个简单的改造:

func main() {
    defer func() {
        fmt.Println("defer func start")
	if r := recover(); r != nil {
	    fmt.Printf("%s \n", r)
	}
	fmt.Println("defer func end")
    }()
    f1()
    fmt.Println("main func end")
}

func f1() {
    fmt.Println("func f1 start")
    arr := []int{}
    fmt.Println(arr[10])
    fmt.Println("func f1 end")
}

输出

func f1 start
defer func start
runtime error: index out of range [10] with length 0 
defer func end

上述代码中,我只是在 main 函数最开头添加了一个 defer 函数,并在该函数中调用了 recover 函数。注意,我们在文章的最开头已经说明了,当程序发生 panic 时,程序会依次执行栈中的 defer 函数(关于 defer 函数请阅读官网描述)。所以当前程序发生 panic 时在进程退出之前会走到 defer 函数中执行 recover 函数,recover 函数会恢复当前进程并打印错误信息。

这里我需要特别提醒你一点,最好将 defer 语句写在函数的最前面。如果上述例子我将 f1 的调用写在 defer 函数之前,你会发现 recover 函数还是没有执行

func main() {
    f1()
    defer func() {
	fmt.Println("defer func start")
	if r := recover(); r != nil {
	    fmt.Printf("%s \n", r)
	}
	fmt.Println("defer func end")
    }()
    fmt.Println("main func end")
}

这是因为 f1 造成 panic 时,defer 函数根本就没有压入函数调用栈中。

何时使用 panic

当你的项目中特别依赖一些组件时,比如一些 web 项目中经常会在进程启动之前初始化一些 mysql,mq 句柄。这些实例对业务来说是非常重要的,所以当这些实例初始化失败时我们可以直接让当前程序 panic(手动 panic),然后及时发现问题并解决。这样总比你带着问题上线后,然后一批流入打入进来,客户端疯狂报错要好

Golang 中手动调用 panic:

func main() {
    initMysql()
}

func initMysql() {
    panic("init mysql failed") // panic 可以接收一个 interface 类型的参数
}

以上关于Golang中panic的实现教程的文章就介绍到这了,更多相关内容请搜索码云笔记以前的文章或继续浏览下面的相关文章,希望大家以后多多支持码云笔记。

「点点赞赏,手留余香」

0

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 admin@mybj123.com 进行投诉反馈,一经查实,立即处理!
重要:如软件存在付费、会员、充值等,均属软件开发者或所属公司行为,与本站无关,网友需自行判断
码云笔记 » Golang中panic的实现教程

发表回复