go defer需要弄清楚的三个点

本篇主要记录defer的三个关键点。

我们知道defer调用会在函数结束时被执行,但除此之外,我们还应该弄清楚几个问题:

函数结束前遇到panic,defer调用还会执行吗?

多个defer的调用顺序是怎么样的?

defer的参数是调用时确定的还是执行时?

下面结合例子,逐个说明一下。

遇到panic时,panic之前的defer会执行

其实这点不算是defer的知识点,算是panic的流程。程序发生panic时,会一层一层的返回,查找有没有recover,返回的过程中遇到的所有defer都会执行,因为recover一定是在defer调用的。可以写段简单的程序试验下。

package main

import "fmt"

func print(a int)  {
	fmt.Println(a)
}

func main()  {
	a, b := 2, 0
	defer print(a)
	fmt.Println(a / b)
	defer print(b)
}

运行结果如下:

a/b时,由于b是0,所以出现了panic,但是panic上面defer调用的print(a)还是执行了,而panic后面的print(b)则没有执行。

多个defer,按照后进先出的顺序执行

defer在调用时,会将执行的内容先存入栈中,到函数执行结束,再将栈中的调用依次取出执行。

我们试验一下,先defer print(a),再defer print(b)。

package main

import "fmt"

func print(a int)  {
	fmt.Println(a)
}

func main()  {
	a, b := 2, 0
	defer print(a)
	defer print(b)
	fmt.Println("main is running")
}

运行结果如下:

可以看到,print(b)在print(a)之前被执行了。

defer调用的参数,是调用时确定的

defer调用如果传入了一个变量,而这个变量在函数运行过程中被改变了,等到函数结束执行defer调用的时候,defer调用的参数,还是调用的时候那个值,这是因为变量的值和defer调用一起,在调用的时候,已经存入了栈中。

还是简单验证一下。

package main

import "fmt"

func print(a int)  {
	fmt.Println(a)
}

func main()  {
	a := 2
	defer print(a)
	a = 3
	fmt.Println("main is running, a=", a)
}

运行结果如下:

可以看到a虽然被改变了,但是最后执行的print(a),打印的还是defer调用时候的值。

发布了39 篇原创文章 · 获赞 25 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/u013536232/article/details/104875767