golang panic原理

要了解panic,建议先去看一下defer的原理,具体不展开,这里只说panic。

//runtime/runtime2.go
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
    recovered bool // whether this panic is over
    aborted bool // the panic was aborted
}

_panic是个结构体,存储了defer指针、参数,panic列表的表头指针,和已恢复或已终止的信息,后续版本还会继续扩展这个结构体,优化panic和recover的性能。以下处理流程,在runtime.gopanic方法中,有兴趣可以直接读代码。这里只是简单描述一下,不涉及过于细节的部分。

0、每个goroutine都有一个panic链表,运行时,遇到panic代码,会生成对应的_panic数据,存到这个链表的表头。
1、每执行完毕一个函数,如果没有panic发生,就跳过对应的_panic数据,回到正常流程,否则进入2。
2、如果有panic发生,处理链表中对应的_panic,进入3。
3、如果defer链表(跟panic链表一样,也是每个goroutine一个)里存在defer,按约定顺序执行延迟代码,进入4,否则进入7。
4、当defer链表执行到需要recover的时候,就交给reflectcall去调用gorecover,进入5,否则进入6。
5、执行recover,这时对应的_panic结构里的recovered字段标记为真,由recovery方法,负责安抚当前的_panic,回到正常流程(这个地方的处理,依赖deferproc方法,自己去读代码)。
6、如果没recover,那就进入死给你看流程,进入7。
7、比较厚道的执行preprintpanics方法,打印出所涉及的 panic 消息,进入8。
8、最后,执行fatalpanic方法,宣布老子不玩了。

套娃问题:当执行defer链表中的defer的时候,可能会有新的panic产生,此时会把当前的_panic标记为放弃,进入新产生的_panic处理流程。

特别提示:因为Golang的gorotuine机制,panic在不同的gorotuine里面,是单独的,并不是整体处理。可能一个地方凉了,就会整体完蛋,这个要非常小心。

你可能感兴趣的:(golang panic原理)