网络异常,图片无法展示
|
底层逻辑
在每个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之后调用才会生效