Go语言 defer

参考文章:深入理解Go语言

defer用于资源的释放,会在函数返回之前进行调用

defer关键字的实现跟go关键字很类似,不同的是它调用的是runtime.deferproc而不是runtime.newproc。
在defer出现的地方,插入了指令call runtime.deferproc,然后在函数返回之前的地方,插入指令call runtime.deferreturn。
普通的函数返回时,汇编代码类似:

add xx SP
return

如果其中包含了defer语句,则汇编代码是:

call runtime.deferreturn,
add xx SP
return

goroutine的控制结构中,有一张表记录defer,调用runtime.deferproc时会将需要defer的表达式记录在表中,而在调用runtime.deferreturn的时候,则会依次从defer表中出栈并执行。

实例一:先按照普通函数执行,然后再按照defer 的先进后出逆向执行

func main() {
   fmt.Println("hello world 123")
   defer goodBye()
   defer goodNight()
   goodEating()
   fmt.Println("hello world")
}

func goodNight()  {
   fmt.Println("goodnight")
}
func goodBye()  {
   fmt.Println("goodbye")
}
func goodEating()  {
   fmt.Println("goodeating")
}
---------output-----------
hello world 123
goodeating
hello world
goodnight
goodbye

实例二:若在中间添加一个 return,那么在return之上的,先按照普通函数执行,然后按照defer 的先进后出逆向执行,在return之下的不执行

func main() {
   fmt.Println("hello world 123")
   defer goodBye()
   defer goodNight()
   goodEating()
   return
   fmt.Println("hello world")
}

func goodNight()  {
   fmt.Println("goodnight")
}
func goodBye()  {
   fmt.Println("goodbye")
}
func goodEating()  {
   fmt.Println("goodeating")
}
------output-------
hello world 123
goodeating
goodnight
goodbye

实例三:返回值操作

func f() (result int) {
    defer func() {
        result++
    }()
    return 0
}

由于 return xxx这一条语句并不是一条原子指令!
所以 return xxx 分为 三步操作 :返回值 = xxx ,调用defer函数,空的return,上面代码可以看作下面的代码

func f() (result int) {
     result = 0  //return语句不是一条原子调用,return xxx其实是赋值+ret指令
     func() { //defer被插入到return之前执行,也就是赋返回值和ret指令之间
         result++
     }()
     return
}

实例四:带参数的赋值返回值操作

func f() (r int) {
     t := 5
     defer func() {
       t = t + 5
     }()
     return t
}

上述的return返回的是 t,而我们func的返回值是 r,上面代码可以看作下面的代码

func f() (r int) {
     t := 5
     r = t //赋值指令
     func() {        //defer被插入到赋值与返回之间执行,这个例子中返回值r没被修改过
         t = t + 5
     }
     return        //空的return指令
}

实例五

func f() (r int) {
    defer func(r int) {
          r = r + 5
    }(r)
    return 1
}

分析:

func f() (r int) {
     r = 1  //给返回值赋值
     func(r int) {        //这里改的r是传值传进去的r,不会改变要返回的那个r值
          r = r + 5
     }(r)
     return        //空的return
}

实例六

func main() {
   i := deferRet(1,1)
   println(i)  // print 152
}

func deferRet(x,y int) (z int){
   defer func () { z += 100 }()
   z = x + y  
   return z + 50 // 执行顺序 z = z+50 -> (call defer)z = z+100 -> ret
}

分析:

func deferRet(x,y int) (z int){
   z = x + y // z=2
   z = z+50   //给返回值赋值z=52
  defer z += 100 //z = 150
  return   
}

本质原因是return xxx语句并不是一条原子指令,defer被插入到了赋值 与 ret之间,因此可能有机会改变最终的返回值。

你可能感兴趣的:(Go语言 defer)