美文网首页
go 的 defer 实现原理

go 的 defer 实现原理

作者: wayyyy | 来源:发表于2022-08-30 00:25 被阅读0次
    defer 需要注意的2点
    • defer 函数的参数在defer 语句出现时就已经确定

      func foo() {
          i := 0
          defer fmt.Println(i)
          i++
          return
      }
      

      输出 i 的值为0

    • defer 函数可以操作所在函数的具名返回值
      关键字 return 并不是一个原子操作,实际上 return 只代表汇编指令ret,即跳转程序执行,比如语句 return i,实际上分为2步执行,即先将i 的值存入栈中作为返回值,然后执行跳转,而defer 语句的执行时机正是在跳转前。

      func foo() (result int) {
          i := 1
          defer func() {
              result++
          }
          
          return i
      }
      

      所以这里return 执行的代码等同于:

      result = i
      result++
      return 
      
    数据结构

    src/runtime/runtime2.go:_defer定义了 defer 数据结构

    type _defer struct {
        ...
        sp uintptr      // 函数栈指针
        pc uintptr      // 程序计数器
        fn *funcval    // 函数地址
        link*_defer    // 指向自身结构的指针,用于形成链表
    }
    

    从其数据结构来看,每个_defer实例实际上是对一个函数的封装,它拥有执行函数的必要信息,比如栈地址,程序计数器,函数地址等。编译器会把每个延迟函数编译成一个_defer实例暂存到数据结构中,待函数结束时再逐个取出执行。

    每个 defer 语句对应一个 _defer 实例,多个实例使用指针链接起来形成一个单链表(头部插入),保存到 数据结构中。

    type g struct {
        ...
        _defer
        ...
    }
    
    image.png
    defer 的创建和执行

    src/runtime/panic.go 定义了一下两个方法,分别用于创建 defer 和 执行 defer

    • deferproc 负责把 defer 函数处理成 _defer实例,并存入 gorountine 中的链表。
    • deferreturn 负责把 从 defer 从 gorountine 链表中的实例取出并执行。
    演进和优化历史

    TODO

    相关文章

      网友评论

          本文标题:go 的 defer 实现原理

          本文链接:https://www.haomeiwen.com/subject/pktlnrtx.html