这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记
1.什么是defer延迟语句
在函数中,经常需要创建资源(比如数据库连接、文件句柄、锁)。为了在函数执行完毕后及时地释放资源,Go的设计者提供defer延迟语句。
defer语句主要用在函数中,用来在函数结束(return或者panic异常导致结束)之前执行某个动作,是一个函数结束前最后执行的动作。
在Go语言一个函数中,defer语句的执行逻辑如下。
1 当程序执行到一个defer时,不会立即执行defer后的语句,而是将defer后的语句压入一个专门存储defer语句的栈中,然后继续执行函数下一个语句。
2 当函数执行完毕后,再从defer栈中依次从栈顶取出语句执行(注:先进去的最后执行,最后进去的最先执行)。
3 在defer将语句放入栈时,也会将相关的值复制进入栈中
package main
import "fmt"
func main() {
deferCall()
}
func deferCall() {
defer func1()
defer func2()
defer func3()
}
func func1() {
fmt.Println("A")
}
func func2() {
fmt.Println("B")
}
func func3() {
fmt.Println("C")
}
以上代码的运行结果为
C
B
A
2.defer与return的执行顺序
来分析一下运行结果:
- 运行结果的第一行很直观,name此时还是全局变量,值还是“go".
- 运行结果的第二行,在defer里改变了这个全局变量,此时name的值已经变成了”python"
- 运行结果的第三行是重点,为什么输出的是“go”?解释只有一个——defer是在return后才调用的。所以在执行defer前,myname已经被赋值成"go"了。 结论: return并非原子操作,共分为赋值、返回值两步操作。第一步先return赋值,第二步再执行defer,第三步执行return返回。
无名返回值函数:return返回值不会被defer操作改变
有名返回值函数:由于返回值在函数定义的时候已经将该变量进行定义,在执行return的时候会先执行返回值保存操作,而后续的defer函数会改变这个返回值(虽然defer是在return之后执行的,但是由于使用的函数定义的变量,所以执行defer操作后对该变量的修改会影响到return的值。
3.defer常见应用场景
1.关闭资源 在创建资源(比如数据库连接,文件句柄,锁)后,需要释放掉资源内存,避免占用内存,系统资源。可以在打开资源的语句的下一行,直接用defer语句提前把关闭资源的操作注册了,这样就会减少程序员忘写关闭资源的情况。
2.和recover()函数一起使用 当程序出现宕机或者遇到panic错误时,recover()函数可以恢复执行,而且不会报告宕机错误。之前说过,defer不但可以在return返回前调用,也可以在程序宕机显示painc错误时,在程序出现宕机之前被执行,依次来恢复程序。