【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之后调用才会生效

目录
相关文章
|
5月前
|
JSON Kubernetes Go
利用 Golang 中的 Recover 处理错误
利用 Golang 中的 Recover 处理错误
|
5月前
|
Go
实验深度理解Go中try...catch...的panic、defer、recover用法
文章通过实验代码演示了Go语言中如何使用panic、defer和recover函数来模拟try...catch...的异常处理机制,并详细解释了每个函数的作用和在异常处理中的使用场景。
43 0
|
5月前
|
Serverless Go
Golang 开发函数计算问题之defer 中的 recover() 没有捕获到 如何解决
Golang 开发函数计算问题之defer 中的 recover() 没有捕获到 如何解决
|
8月前
|
Go 开发者
Golang深入浅出之-Go语言 defer、panic、recover:异常处理机制
Go语言中的`defer`、`panic`和`recover`提供了一套独特的异常处理方式。`defer`用于延迟函数调用,在返回前执行,常用于资源释放。它遵循后进先出原则。`panic`触发运行时错误,中断函数执行,直到遇到`recover`或程序结束。`recover`在`defer`中捕获`panic`,恢复程序执行。注意避免滥用`defer`影响性能,不应对可处理错误随意使用`panic`,且`recover`不能跨goroutine捕获panic。理解并恰当使用这些机制能提高代码健壮性和稳定性。
179 2
|
8月前
|
Go
Go语言中的异常处理:理解panic与recover
【2月更文挑战第7天】Go语言虽然以简洁和直接错误处理机制而著称,但它也提供了`panic`和`recover`这两个内置函数来处理程序中的异常情况。本文将深入探讨Go语言中的异常处理机制,包括`panic`和`recover`的使用场景、原理以及最佳实践,帮助读者更好地理解如何在Go中处理异常情况。
|
8月前
|
存储 编译器 Go
Golang底层原理剖析之panic与recover
Golang底层原理剖析之panic与recover
77 0
|
8月前
|
网络协议 BI Go
Go-异常处理(defer recover panic)
Go-异常处理(defer recover panic)
84 0
|
4月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
152 4
Golang语言之管道channel快速入门篇
|
4月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
75 4
Golang语言文件操作快速入门篇
|
4月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
121 3
Golang语言之gRPC程序设计示例