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链表头部
image.png
runtime.deferreturn函数负责从Goroutine的_defer链表中顺序遍历并执行被延迟的函数

Panic和Recover

函数调用panic时会立刻停止执行函数的其他代码,然后处理当前goroutine的_derfer链表,执行完毕后退出整个程序。
结合defer,panic可以做到有序撤退,在defer中结合recover可以达到try…catch的效果

  • 执行流程

编译器将panic关键字翻译为runtime.gopanic并执行:

  1. 把当前panic行为追加到对应goroutine的_panic链表头
  2. 遍历该goroutine的_defer链表,并逐个执行被延迟的函数
  3. 所有延迟函数执行完毕后,调用runtime.fatalpanic函数遍历该goroutine的_panic链表,中止程序,打印崩溃信息,返回错误码2

    程序的崩溃恢复

    当延迟函数runtime.deferproc或runtime.deferprocStack执行时遇到gorecover时,会将_panic.recoverd标记为true,并返回panic的参数;

  4. 在这次调用结束后,gopanic会从_defer中调用recovery函数进行恢复程序

  5. recovery执行完后再跳到_defer链表,执行接下来的被延迟的函数

参考

深入理解Go defer——煎鱼
理解Go语言关键字defer的原理
Golang:深入理解panic and recover