zoukankan      html  css  js  c++  java
  • go defer

    最近看到一道Go语言的面试题,如下:

    package main
    import (
      "fmt"
    )
    func main() {
      defer_call()
    }
    func defer_call() {
      defer func() { fmt.Println("打印前") }()
      defer func() { fmt.Println("打印中") }()
      defer func() { fmt.Println("打印后") }()
      panic("触发异常")
    }
    

    我们来公布一下答案:

    打印后
    打印中
    打印前
    panic: 触发异常
    defer
    可以看出,Go语言中的defer函数是一个后进先出的机制。为什么会这个样子呢,我们先来看一下defer的实现:
    //

    type _defer struct {
      siz int32
      started bool
      heap bool
      openDefer bool
      sp uintptr // 栈指针
      pc uintptr // 调用方的程序计数器
      fn *funcval // 传入的函数
      _panic *_panic
      link *_defer // 指向下一个执行的defer函数
      fd unsafe.Pointer
      varp uintptr
      framepc uintptr
    }
    

    这个是defer的定义,我们主要关注link这个属性,link指向了下一个defer函数,这说明defer使用的是链表这种数据结构,那么我们再看一下defer的link是怎么建立的吧。
    defer的初始化其实是发生在deferproc的newdefer方法,至于为什么,由于不是本文的重点,所以这里就不做过多的描述,可以参考golang中defer的编译调用过程:
    那么我们重点看一下newdefer这个函数:
    //

    func newdefer(siz int32) *_defer {
      var d *_defer
      sc := deferclass(uintptr(siz))
      gp := getg() // 获得当前的goroutine
      ...
      d.link = gp._defer // 现在新的defer函数的link指向了当前的defer函数
      gp._defer = d // 新的defer函数现在是第一个被调用的函数了
      return d
    }
    

    从这里看出,每建立一个新的defer函数,都会把新defer函数的link指向之前的defer函数,同时把新defer函数作为当前goroutine第一个被调用的函数。这是一个典型的链表生成的栈。当然上面只是一个压栈的过程,defer函数并没有执行,真正执行是在deferreturn中,这是由Go语言的编译过程决定的的,具体可以参考上面的链接。
    那我们再看一下deferreturn这个函数:
    //

    func deferreturn(arg0 uintptr) {
      gp := getg() // 获得当前的goroutine
      d := gp._defer
      if d == nil { // 如果没有defer函数,直接return
        return
      }
      ...
      fn := d.fn // 获得defer的func函数
      d.fn = nil // 重置
      gp._defer = d.link // 将前一个defer函数attach到当前goroutine
      freedefer(d) // 释放defer函数
      _ = fn.fn // 执行defer的func函数
      jmpdefer(fn, uintptr(unsafe.Pointer(&arg0)))
    }
    

    这里我们可以看出,defer的调用过程是一个出栈的过程,所以一开始面试题defer的输入就可以理解了。

  • 相关阅读:
    聊聊LiteOS事件模块的结构体、初始化及常用操作
    有了这个数据强一致“利器”,DBA们轻松修复数据对加班“say no”
    开源一周岁,MindSpore新特性巨量来袭
    90后就敢当扫地僧!不服?4月24号来闯龙门阵!
    你的数仓函数结果不稳定,可能是属性指定错了
    云小课 | 不了解EIP带宽计费规则?看这里!
    腾讯 angel 3.0:高效处理模型
    深度树匹配模型(TDM)
    X-Deep Learning功能模块
    XLearning
  • 原文地址:https://www.cnblogs.com/frankltf/p/13823259.html
Copyright © 2011-2022 走看看