Golang中的panic

简介: 本文介绍了Golang中的panic机制,包括如何通过defer和recover恢复致命异常,分析panic堆栈信息,并探讨了何时应使用panic。内容实用,适合希望深入理解Go错误处理机制的开发者。

前言

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

分析造成panic堆栈信息

go

体验AI代码助手

代码解读

复制代码

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产生的具体原因。下面我们通过代码来进行演示

go

体验AI代码助手

代码解读

复制代码

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函数根本没有机会执行。 下面我将上述代码进行一个简单的改造:

go

体验AI代码助手

代码解读

复制代码

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")
}

输出

go

体验AI代码助手

代码解读

复制代码

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函数还是没有执行

go

体验AI代码助手

代码解读

复制代码

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:

go

体验AI代码助手

代码解读

复制代码

func main() {
	initMysql()
}

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


转载来源:https://juejin.cn/post/7084869136727146527

目录
打赏
0
5
5
0
207
分享
相关文章
Golang 语言怎么使用 panic 函数?
Golang 语言怎么使用 panic 函数?
152 0
Golang深入浅出之-Go语言 defer、panic、recover:异常处理机制
Go语言中的`defer`、`panic`和`recover`提供了一套独特的异常处理方式。`defer`用于延迟函数调用,在返回前执行,常用于资源释放。它遵循后进先出原则。`panic`触发运行时错误,中断函数执行,直到遇到`recover`或程序结束。`recover`在`defer`中捕获`panic`,恢复程序执行。注意避免滥用`defer`影响性能,不应对可处理错误随意使用`panic`,且`recover`不能跨goroutine捕获panic。理解并恰当使用这些机制能提高代码健壮性和稳定性。
333 2
Golang底层原理剖析之panic与recover
Golang底层原理剖析之panic与recover
133 0
Golang 语言怎么避免空指针引发的 panic
Golang 语言怎么避免空指针引发的 panic
218 0
Golang 语言怎么避免引发 panic?
Golang 语言怎么避免引发 panic?
172 0
Golang中的管道(channel) 、goroutine与channel实现并发、单向管道、select多路复用以及goroutine panic处理
Golang中的管道(channel) 、goroutine与channel实现并发、单向管道、select多路复用以及goroutine panic处理
586 0
【Golang】panic和recover底层逻辑实现|Go主题月
在每个goroutine也有一个指针指向_panic链表表头,然后每增加一个panic就会在链表头部加入一个_panic结构体。当所有的defer执行完后,_panic链表就会从尾部开始打印panic信息了,也就是说先发生的panic先打印信息。
322 0
【Golang】panic和recover作用|Go主题月
panic:一旦出现,就意味着程序的结束并退出。Go 语言中 panic 关键字主要用于主动抛出异常,类似 java 等语言中的 throw 关键字。 recover:将程序状态从严重的错误中恢复到正常状态。Go 语言中 recover 关键字主要用于捕获异常,让程序回到正常状态,类似 java 等语言中的 try ... catch 。
632 0
Golang 中比较常见的 panic 异常原因之一
在 Golang 中,当异常发生时不管是主动触发 panic 还是由于编码错误导致的 panic,我们都可以使用 recover 进行捕获。当时前提必须定义 defer 语句,且 defer 必须放在 panic 之前定义,另外 recover 只有在 defer 调用的函数中才有效。
1370 0
Golang 中比较常见的 panic 异常原因之一
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等