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 可能会导致性能问题,尤其是在一个 “大循环” 里。

猜你喜欢

转载自blog.csdn.net/qq_43762191/article/details/125339804