Go defer 的一些注意事项

defer 碰上闭包

package main

import "fmt"

func main() {
	var whatever [5]struct{}
	for i := range whatever {
		defer func() { fmt.Println(i) }()
	}
}
4
4
4
4
4
package main

import "fmt"

func f(i int) {
	fmt.Println(i)
}

func main() {
	var whatever [5]struct{}
	for i := range whatever {
		defer f(i)
	}
}
4
3
2
1
0

其实go说的很清楚,我们一起来看看go spec如何说的

Each time a “defer” statement executes, the function value and parameters to the call are evaluated as usualand saved anew but the actual function is not invoked.

也就是说函数正常执行,由于闭包用到的变量 i 在执行的时候已经变成4,所以输出全都是4.


defer 碰上指针

这个大家用的都很频繁,但是go语言编程举了一个可能一不小心会犯错的例子.

package main

import "fmt"

type Test struct {
	name string
}

func (t *Test) Close() {
	fmt.Println(t.name, " closed")
}
func main() {
	ts := []Test{{"a"}, {"b"}, {"c"}}
	for _, t := range ts {
		defer t.Close()
	}
}
c  closed
c  closed
c  closed

这个输出并不会像我们预计的输出c b a,而是输出c c c

可是按照前面的go spec中的说明,应该输出c b a才对啊.

那我们换一种方式来调用一下.

package main

import "fmt"

type Test struct {
	name string
}

func (t *Test) Close() {
	fmt.Println(t.name, " closed")
}
func Close(t Test) {
	t.Close()
}
func main() {
	ts := []Test{{"a"}, {"b"}, {"c"}}
	for _, t := range ts {
		defer Close(t)
	}
}
c  closed
b  closed
a  closed

这个时候输出的就是c b a
当然,如果你不想多写一个函数,也很简单,可以像下面这样,同样会输出c b a
看似多此一举的声明

package main

import "fmt"

type Test struct {
	name string
}

func (t *Test) Close() {
	fmt.Println(t.name, " closed")
}
func main() {
	ts := []Test{{"a"}, {"b"}, {"c"}}
	for _, t := range ts {
		t2 := t
		defer t2.Close()
	}
}
c  closed
b  closed
a  closed

结论

通过以上例子,结合

Each time a “defer” statement executes, the function value and parameters to the call are evaluated as usualand saved anew but the actual function is not invoked.

这句话。可以得出下面的结论:

defer后面的语句在执行的时候,函数调用的参数会被保存起来,但是不执行。也就是复制了一份。但是并没有说struct这里的this指针如何处理,通过这个例子可以看出go语言并没有把这个明确写出来的this指针当作参数来看待。


中道崩殂??

不存在的。。

多个 defer 注册,按 FILO 次序执行 ( 先进后出 )。哪怕函数或某个延迟调用发生错误,这些调用依旧会被执行。

package main

func test(x int) {
	defer println("a")
	defer println("b")
	defer func() {
		println(100 / x) // div0 异常未被捕获,逐步往外传递,最终终止进程。
	}()
	defer println("c")
}
func main() {
	test(0)
}
c
b
a
panic: runtime error: integer divide by zero

goroutine 1 [running]:
main.test.func1()
        C:/Users/Administrator/Desktop/go/main.go:7 +0x65
main.test(0xc00003e000?)
        C:/Users/Administrator/Desktop/go/main.go:10 +0x96
main.main()
        C:/Users/Administrator/Desktop/go/main.go:12 +0x1b

*滥用 defer 可能会导致性能问题,尤其是在一个 “大循环” 里。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

看,未来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值