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的输入就可以理解了。

  • 相关阅读:
    机器学习(深度学习)
    机器学习(六)
    机器学习一-三
    Leetcode 90. 子集 II dfs
    Leetcode 83. 删除排序链表中的重复元素 链表操作
    《算法竞赛进阶指南》 第二章 Acwing 139. 回文子串的最大长度
    LeetCode 80. 删除有序数组中的重复项 II 双指针
    LeetCode 86 分割链表
    《算法竞赛进阶指南》 第二章 Acwing 138. 兔子与兔子 哈希
    《算法竞赛进阶指南》 第二章 Acwing 137. 雪花雪花雪花 哈希
  • 原文地址:https://www.cnblogs.com/frankltf/p/13823259.html
Copyright © 2011-2022 走看看