golang之defer

来源:互联网 发布:java选课源代码 编辑:程序博客网 时间:2024/06/10 23:26
                                                                                                     

点击上方蓝色字体关注我们

golang之defer

golang 中的延迟调用函数


延迟调用函数(Deferred Function Calls)的调用方式如下:

defer func_name(param-list)


1 执行的时机


func f1() (ret int) {

defer func() {

ret++

}()

return 0

}

 

func f2() (ret int) {

tmp := 1

defer func() {

tmp = tmp + 10

}()

return tmp

}

 

func f3() (ret int) {

defer func(ret int) {

ret = ret + 10

}(ret)

return 1

}

 

先不要运行程序,心里记下自己认为分别运行这三个函数的结果,然后我们慢慢分析。


golang之defer


这里先说结论:


网上有篇博文讲的defer的实现方式就是在defer出现的地方插入指令:

CALL runtime.deferproc


然后在函数返回之前的地方插入指令:

CALL runtime.deferreturn

 

同时由于 return ret这一句并不是原子操作,它是分为两步来执行的:


1)先在栈中写一个值,这个值被当作返回值;


2)然后调用空的return语句。


那么有了defer之后,defer的执行是被插入在return之前赋值指令之后的。


有了这个结论,我们来分析前面的代码的结果:


f1():先给返回的值ret=0赋值,然后执行defer,把ret++,这样最后返回出来的结果就是 1 ;


f2():return之前 tmp的值是1,执行ret = 1,然后执行defer,把tmp+=10,此时在返回,不影响返回结果,最终结果还是1;


f3():return之前,先执行ret=1的赋值操作,然后,将ret值传递给匿名函数,执行的结果不影响之前的值,所以ret函数最终的返回值是1。

 

2 执行的顺序


func f4() {

defer func() {

fmt.Println("1-1")

defer func() {

fmt.Println("1-1-1")

}()

}()

 

defer func() {

fmt.Println("2-2")

defer func() {

fmt.Println("2-2-2")

}()

}()

}


猜猜看,调用f4之后出现的打印是什么:

结论是


golang之defer
 

可以看出它是基于先进后出的方式存储defer函数的,也就是逆序执行的


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

 

3 使用defer值得注意的地方

 

1 经常在加锁的地方紧接着使用 defer 去解锁,这样写是非常方便,不用在加锁之后,每个return的地方都要去解锁,但是别忘了加锁之后要是有阻塞的操作,导致函数一直没有办法返回,那么就出现了问题。


func f5() {

lc.Lock()

defer lc.Unlock()

for { // 长期执行某个操作

if 0 { // 达到某种条件之后退出

break

}

}

return

}


如上f5(),如果lc这把锁有其他的地方在用,那么就导致了死锁的问题。



golang之defer
扫描二维码关注我们




查看原文:http://www.zoues.com/2016/10/20/golang%e4%b9%8bdefer/
0 0
原创粉丝点击