Go defer 的一些注意事项

Posted 看,未来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 可能会导致性能问题,尤其是在一个 “大循环” 里。

以上是关于Go defer 的一些注意事项的主要内容,如果未能解决你的问题,请参考以下文章

Go语言中defer的一些坑

初识 go 语言:语法

Go中defer的延迟调用

Go ---- defer 和 return 执行的先后顺序

golang中defer的正确使用方式(源自深入解析go)

Go语言中的defer