网络异常,图片无法展示
|
底层逻辑
在每个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 }
网络异常,图片无法展示
|
结构体中的 pc
、sp
和 goexit
三个字段都是为了修复 runtime.Goexit
带来的问题引入的。runtime.Goexit
能够只结束调用该函数的 Goroutine 而不影响其他的 Goroutine,但是该函数会被 defer
中的 panic
和 recover
取消,引入这三个字段就是为了保证该函数的一定会生效。
嵌套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
要通过defer
在panic
之后调用才会生效