Go异常处理机制panic和recover

recover

使用panic抛出异常后, 将立即停止当前函数的执行并运行所有被defer的函数,然后将panic抛向上一层,直至程序crash。但是也可以使用被defer的recover函数来捕获异常阻止程序的崩溃,recover只有被defer后才是有意义的。

go 复制代码
func main() {

	print(123)

	print(456)
	panic("throw an error")

	print(678) //IDE会有提示: Unreachable code

}

结果:

go 复制代码
123456panic: throw an error

goroutine 1 [running]:
main.main()
    /Users/shuangcui/explore/panicandrecover.go:31 +0x67

使用recover()捕获异常:

go 复制代码
func main() {

	print(123)

	defer func() {
		if err := recover(); err != nil {
			print("recover it")
		}
	}()

	print(456)
	panic("throw an error")

	print(678) //IDE会有提示: Unreachable code

}

结果为:

go 复制代码
123456recover it

如果有两个recover,则捕获异常的是后一个

go 复制代码
func main() {

	print(123)

	defer func() {
		if err := recover(); err != nil {
			print("recover it")
		}
	}()

	defer func() {
		if err := recover(); err != nil {
			print("复原!")
		}
	}()

	print(456)
	panic("throw an error")

	print(678) //IDE会有提示: Unreachable code

}

结果为:

go 复制代码
123456复原!

panic之后的任何代码都不会继续执行

前提是panic不在if里面

go 复制代码
package main

import "fmt"

func main() {
	defer_call()
	fmt.Println("333 Helloworld")
}

func defer_call() {
	defer func() {
		fmt.Println("11111")
	}()

	defer func() {
		fmt.Println("22222")
	}()

	defer func() {
		if r := recover(); r != nil {
			fmt.Println("Recover from r : ", r)
		}
	}()

	defer func() {
		fmt.Println("33333")
	}()

	fmt.Println("111 Helloworld")

	panic("Panic 1!")


    //使用panic抛出异常后, 将立即停止当前函数的执行并运行所有被defer的函数,然后将panic抛向上一层, 直至程序crash

    //但是也可以使用被defer的recover函数来捕获异常阻止程序的崩溃,recover只有被defer后才是有意义的。

	panic("Panic 2!") //panic1之后的panic2没有任何机会会被执行, panic2之后的任何代码更没有任何机会被执行

	fmt.Println("222 Helloworld")
}

输出为:

go 复制代码
111 Helloworld
33333
Recover from r :  Panic 1!
22222
11111
333 Helloworld

对于goroutine中的panic,协程外面的recover是无法恢复的;goroutine中的recover,同样无法恢复协程外的panic

但协程中的recover可以恢复协程中的panic

go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {

	go func() {
		defer func() {
			if err := recover(); err != nil {
				fmt.Println("recover err:", err)
			}
		}()
		panic("里面出错了")
	}()

	//panic("外面出错了")

	time.Sleep(1 * time.Second)

}

输出为:

recover err 里面出错了

主方法中的recover,也可以恢复子方法里的panic

但如果go subfunc(),则同样无法捕获subfunc中的异常

go 复制代码
func main() {

	fmt.Println(123)

	defer fmt.Println(999)

	defer func() {
		if err := recover(); err != nil {
			fmt.Println("恢复异常:",err)
		}

	}()
	subfunc()

}

func subfunc() {

	defer fmt.Println(888)
	panic("出现了bug")

	defer fmt.Println(456)

}

结果为:

go 复制代码
123
888
恢复异常: 出现了bug
999

因为panic发生的时候,panic函数后面的语句都不会执行了,所以recover函数不能放在panic语句后面执行,而要放在defer函数中执行。
使用 panic 抛出异常后,函数执行将从调用 panic 的地方停止,如果函数内有 defer 调用,则执行 defer 后边的函数调用,如果 defer 调用的函数中没有捕获异常信息,这个异常会沿着函数调用栈往上传递,直到 main 函数仍然没有捕获异常,将会导致程序异常退出

如何区别使用 panic 和 error 两种方式?

惯例是:导致关键流程出现不可修复性错误的使用 panic ,其他使用 error 。

panic 和 recover 的组合有如下特性:

  • 有 panic 没 recover ,程序宕机。
  • 有 panic 也有 recover ,程序不会宕机,执行完对应的 defer 后,从宕机点退出当前函数后继续执行。

recover能捕获所有错误吗?

不能!

Go 有哪些无法恢复的致命场景?

  • 并发读写 map fatal error: concurrent map read and map write
  • 堆栈内存耗尽(如递归)
go 复制代码
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc0200e1bf0 stack=[0xc0200e0000, 0xc0400e0000]
fatal error: stack overflow
  • 将 nil 函数作为 goroutine 启动 fatal error: go of nil func value
  • goroutines 死锁 fatal error: all goroutines are asleep - deadlock!
  • 线程超过设置的最大限制 fatal error: thread exhaustion
  • 超出可用内存 fatal error: runtime: out of memory

总之 都会报fatal error:xxxxxxxx

拓展&参考:

golang panic和recover 实现原理

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

Go 语言踩坑记------panic 与 recover

相关推荐
野犬寒鸦10 分钟前
Linux常用命令详解(下):打包压缩、文本编辑与查找命令
linux·运维·服务器·数据库·后端·github
huohuopro30 分钟前
thinkphp模板文件缺失没有报错/thinkphp无法正常访问控制器
后端·thinkphp
cainiao0806053 小时前
《Spring Boot 4.0新特性深度解析》
java·spring boot·后端
-曾牛3 小时前
Spring AI 与 Hugging Face 深度集成:打造高效文本生成应用
java·人工智能·后端·spring·搜索引擎·springai·deepseek
南玖yy4 小时前
C/C++ 内存管理深度解析:从内存分布到实践应用(malloc和new,free和delete的对比与使用,定位 new )
c语言·开发语言·c++·笔记·后端·游戏引擎·课程设计
计算机学姐4 小时前
基于SpringBoot的小区停车位管理系统
java·vue.js·spring boot·后端·mysql·spring·maven
BUG制造机.4 小时前
Go 语言 slice(切片) 的使用
开发语言·后端·golang
小鸡脚来咯5 小时前
请求参数:Header 参数,Body 参数,Path 参数,Query 参数分别是什么意思,什么样的,分别通过哪个注解获取其中的信息
java·spring boot·后端
天上掉下来个程小白6 小时前
添加购物车-02.代码开发
java·服务器·前端·后端·spring·微信小程序·苍穹外卖
幽络源小助理7 小时前
懒人美食帮SpringBoot订餐系统开发实现
java·spring boot·后端·美食