【Golang】panic和recover底层逻辑实现|Go主题月

简介: 在每个goroutine也有一个指针指向_panic链表表头,然后每增加一个panic就会在链表头部加入一个_panic结构体。当所有的defer执行完后,_panic链表就会从尾部开始打印panic信息了,也就是说先发生的panic先打印信息。

网络异常,图片无法展示
|

底层逻辑

在每个goroutine也有一个指针指向_panic链表表头,然后每增加一个panic就会在链表头部加入一个_panic结构体。当所有的defer执行完后,_panic链表就会从尾部开始打印panic信息了,也就是说先发生的panic先打印信息。

网络异常,图片无法展示
|

_panic结构体

go源码的runtime/runtime2.go中有_panic的结构体信息

type _panic struct {
  argp      unsafe.Pointer // pointer to arguments of deferred call run during panic; cannot move - known to liblink
  arg       interface{}    // argument to panic
  link      *_panic        // link to earlier panic
  pc        uintptr        // where to return to in runtime if this panic is bypassed
  sp        unsafe.Pointer // where to return to in runtime if this panic is bypassed
  recovered bool           // whether this panic is over
  aborted   bool           // the panic was aborted
  goexit    bool
}

网络异常,图片无法展示
|

结构体中的 pcspgoexit 三个字段都是为了修复 runtime.Goexit 带来的问题引入的。runtime.Goexit 能够只结束调用该函数的 Goroutine 而不影响其他的 Goroutine,但是该函数会被 defer 中的 panicrecover 取消,引入这三个字段就是为了保证该函数的一定会生效。

嵌套panic

接下来用个嵌套的panic例子来强化理解一下上面讲的

package main
func main() {
  defer func() {
    defer func() {
      panic("双重嵌套:panic")
    }()
    panic("panic1")
  }()
  defer func() {
    panic("panic2")
  }()
  panic("main-panic")
}

输出:

panic: main-panic
        panic: panic2
        panic: panic1
        panic: 双重嵌套:panic
goroutine 1 [running]:
main.main.func1.1()
        /home/zheng/STUDY/GoWork/demo/main.go:6 +0x39
panic(0x466460, 0x48a2b8)
        /usr/local/go/src/runtime/panic.go:965 +0x1b9
main.main.func1()
        /home/zheng/STUDY/GoWork/demo/main.go:8 +0x5b
panic(0x466460, 0x48a2c8)
        /usr/local/go/src/runtime/panic.go:965 +0x1b9
main.main.func2()
        /home/zheng/STUDY/GoWork/demo/main.go:11 +0x39
panic(0x466460, 0x48a298)
        /usr/local/go/src/runtime/panic.go:965 +0x1b9
main.main()
        /home/zheng/STUDY/GoWork/demo/main.go:13 +0x68

图解:

网络异常,图片无法展示
|

recovery

这个函数的目的很简单,就是把_panic结构体中的recovered字段设为true,不过它在执行修改前会判断当前有没有panic和当前panic有没有被恢复过。可以通过源码runtime.gorecover中体现出来

func gorecover(argp uintptr) interface{} {
  gp := getg()
  p := gp._panic
  if p != nil && !p.recovered && argp == uintptr(p.argp) {
    p.recovered = true
    return p.arg
  }
  return nil
}

所以recovery要通过deferpanic之后调用才会生效

目录
相关文章
|
1月前
|
Java 编译器 Go
【Golang】(1)Go的运行流程步骤与包的概念
初次上手Go语言!先来了解它的运行流程吧! 在Go中对包的概念又有怎样不同的见解呢?
91 4
|
1月前
|
Java 编译器 Go
【Golang】(5)Go基础的进阶知识!带你认识迭代器与类型以及声明并使用接口与泛型!
好烦好烦好烦!你是否还在为弄不懂Go中的泛型和接口而烦恼?是否还在苦恼思考迭代器的运行方式和意义?本篇文章将带你了解Go的接口与泛型,还有迭代器的使用,附送类型断言的解释
114 3
|
1月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
92 1
|
4月前
|
人工智能 关系型数据库 MySQL
Golang中的panic
本文介绍了Golang中的panic机制,包括如何通过defer和recover恢复致命异常,分析panic堆栈信息,并探讨了何时应使用panic。内容实用,适合希望深入理解Go错误处理机制的开发者。
113 5
|
4月前
|
人工智能 测试技术 持续交付
Golang深入浅出之-Go语言中的持续集成与持续部署(CI/CD)
持续集成与持续部署(CI/CD)是现代软件开发的关键实践,尤其适用于Go语言项目。本文探讨了Go项目中常见的CI/CD问题,如测试覆盖不足、版本不一致和构建时间过长,并提供解决方案及GitHub Actions示例代码,帮助开发者优化流程,提升交付效率和质量。
126 5
|
8月前
|
Go 开发者
go-carbon v2.6.0 重大版本更新,轻量级、语义化、对开发者友好的 golang 时间处理库
carbon 是一个轻量级、语义化、对开发者友好的 Golang 时间处理库,提供了对时间穿越、时间差值、时间极值、时间判断、星座、星座、农历、儒略日 / 简化儒略日、波斯历 / 伊朗历的支持
150 3
|
12月前
|
JSON Go 开发者
go-carbon v2.5.0 发布,轻量级、语义化、对开发者友好的 golang 时间处理库
carbon 是一个轻量级、语义化、对开发者友好的 Golang 时间处理库,提供了对时间穿越、时间差值、时间极值、时间判断、星座、星座、农历、儒略日 / 简化儒略日、波斯历 / 伊朗历的支持。
239 4
|
存储 Cloud Native Shell
go库介绍:Golang中的Viper库
Viper 是 Golang 中的一个强大配置管理库,支持环境变量、命令行参数、远程配置等多种配置来源。本文详细介绍了 Viper 的核心特点、应用场景及使用方法,并通过示例展示了其强大功能。无论是简单的 CLI 工具还是复杂的分布式系统,Viper 都能提供优雅的配置管理方案。
299 6
|
Unix Linux Go
go进阶编程:Golang中的文件与文件夹操作指南
本文详细介绍了Golang中文件与文件夹的基本操作,包括读取、写入、创建、删除和遍历等。通过示例代码展示了如何使用`os`和`io/ioutil`包进行文件操作,并强调了错误处理、权限控制和路径问题的重要性。适合初学者和有经验的开发者参考。
204 4
|
Go
golang语言之go常用命令
这篇文章列出了常用的Go语言命令,如`go run`、`go install`、`go build`、`go help`、`go get`、`go mod`、`go test`、`go tool`、`go vet`、`go fmt`、`go doc`、`go version`和`go env`,以及它们的基本用法和功能。
391 6

推荐镜像

更多