企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# defer实现原理 ## 1 defer数据结构 源码包`src/src/runtime/runtime2.go:_defer`定义了defer的数据结构: ~~~go type _defer struct { sp uintptr //函数栈指针 pc uintptr //程序计数器 fn *funcval //函数地址 link *_defer //指向自身结构的指针,用于链接多个defer } ~~~ 我们知道defer后面一定要接一个函数的,所以defer的数据结构跟一般函数类似,也有栈地址、程序计数器、函数地址等等。 与函数不同的一点是它含有一个指针,可用于指向另一个defer,每个goroutine数据结构中实际上也有一个defer指针,该指针指向一个defer的单链表,每次声明一个defer时就将defer插入到单链表表头,每次执行defer时就从单链表表头取出一个defer执行。 下图展示多个defer被链接的过程: ![](https://img.kancloud.cn/e8/df/e8dfc2964738d16d3fbdc088ffa47d9f_681x436.png) 从上图可以看到,新声明的defer总是添加到链表头部。 函数返回前执行defer则是从链表首部依次取出执行,不再赘述。 一个goroutine可能连续调用多个函数,defer添加过程跟上述流程一致,进入函数时添加defer,离开函数时取出defer,所以即便调用多个函数,也总是能保证defer是按LIFO方式执行的 ## 2 defer的创建和执行 源码包`src/runtime/panic.go`定义了两个方法分别用于创建defer和执行defer。 * deferproc(): 在声明defer处调用,其将defer函数存入goroutine的链表中; * deferreturn():在return指令,准确的讲是在ret指令前调用,其将defer从goroutine链表中取出并执行。 可以简单这么理解,在编译阶段,声明defer处插入了函数deferproc(),在函数return前插入了函数deferreturn()