2020-12-07

Countingstarss · · 290 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Golang中defer与匿名函数共同产生的坑

1. 三种在defer中引用i运行结果的区别

1.1 defer运行的匿名函数采用传值

func multiDefer() {
    i := 1
    defer func(val int) {
        fmt.Println("use defer1,i is", val)
    }(i)
    i++
    defer func(val int) {
        fmt.Println("use defer2,i is", val)
    }(i)
}

运行结果为:

use defer2,i is 2
use defer1,i is 1

1.2 defer运行的匿名函数直接引用外部的i

func multiDefer() {
    i := 1
    defer func() {
        fmt.Println("use defer3,i is", i)
    }()
    i++
    defer func() {
        fmt.Println("use defer4,i is", i)
    }()
}

运行结果为:

use defer4,i is 2
use defer3,i is 2

1.3 defer运行的匿名函数先将i赋值给temp再引用temp

func multiDefer() {
    i := 1
    defer func() {
        temp := i
        fmt.Println("use defer5,i is", temp)
    }()
    i++
    defer func() {
        temp := i
        fmt.Println("use defer6,i is", temp)
    }()
}

运行结果为:

use defer6,i is 2
use defer5,i is 2

2. 运行结果分析

​ 从以上的运行结果不难看出,三种代码对use defer1,use defer2(以及类推的use defer3,use defer4等)的打印符合defer关键字的LIFO特性。

​ 然而在后两种代码中,两次defer中打印的数字都是2 2,只有第一种代码打印的数字为1 2,我们所期望的也是打印1 2,这种现象是怎么产生的呢?

3. 原因分析

​ 首先我们知道,defer调用的部分遵循LIFO的原则,不难联想到,在这个实现的过程中利用了stack,在执行过程中执行到defer时将所需要运行的内容压入stack即可。

​ 那么此时就要考虑压入栈的内容了,在入栈的过程中,压入的包括两部分:执行的代码和函数参数。而我们现在再看第二三段代码,defer都是调用无参数的匿名函数;而第一段代码中defer调用的是有参数的匿名函数。

​ 观察第一段代码,defer运行有参数的匿名函数,执行到defer时的i数值通过函数参数压入了栈,而在defer实际调用过程中,压入栈的函数参数被取出,这时可以正确的被打印。

​ 观察第二三段代码,defer运行无参数的匿名函数,执行到defer时,调用匿名函数过程中显示了闭包的特性,匿名函数可以直接访问外部的变量,因此他们都是在各自的两次defer中打印了当时的i,最终两次显示了i is 2。

4. 总结

​ 运用defer时,要考虑好匿名函数的闭包特性,在执行到defer时,代码和函数参数被压入了栈,匿名函数在函数结束后出栈执行时会体现LIFO的特性。

​ 文字功力差,望谅解,希望多交流。


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:Countingstarss

查看原文:2020-12-07

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

290 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传