Defer
defer的最大作用是panic后依然有效,使用defer需要根据实际情况判断是否需要”延迟”,
编译器在遇到defer
关键字会做两个操作:
- 编译器选择将其转换为
runtime.deferproc
还是runtime.deferprocStack(v1.13提出)
大概思路为:如果defer的函数是顶级函数(没有迭代,e.loopdepth==1),则将_defer分配至栈上,编译器将其翻译为runtime.deferprocStack
,否则被翻译为runtime.deferproc
- 在调用defer关键字的函数返回之前插入
runtime.deferreturn
在程序执行时:runtime.deferproc
函数负责将被延迟的函数追加到对应Goroutine的_defer
链表头部runtime.deferreturn
函数负责从Goroutine的_defer
链表中顺序遍历并执行被延迟的函数
Panic和Recover
函数调用panic时会立刻停止执行函数的其他代码,然后处理当前goroutine的_derfer链表,执行完毕后退出整个程序。
结合defer,panic可以做到有序撤退,在defer中结合recover可以达到try…catch的效果
- 执行流程
编译器将panic
关键字翻译为runtime.gopanic
并执行:
- 把当前
panic
行为追加到对应goroutine的_panic
链表头 - 遍历该goroutine的
_defer
链表,并逐个执行被延迟的函数 所有延迟函数执行完毕后,调用
runtime.fatalpanic
函数遍历该goroutine的_panic链表,中止程序,打印崩溃信息,返回错误码2程序的崩溃恢复
当延迟函数runtime.deferproc或runtime.deferprocStack执行时遇到gorecover时,会将_panic.recoverd标记为true,并返回panic的参数;
在这次调用结束后,gopanic会从_defer中调用recovery函数进行恢复程序
- recovery执行完后再跳到_defer链表,执行接下来的被延迟的函数
参考
深入理解Go defer——煎鱼
理解Go语言关键字defer的原理
Golang:深入理解panic and recover