学习笔记——defer调用(21)"/>
GO学习笔记——defer调用(21)
defer调用也是一种流程控制语句,经常用来调用一些资源处理函数。
defer语句确保被执行的语句具有下面的调用时机
defer调用必须出现在函数内,并且在该函数返回之前才回去执行defer调用的函数
给一个示例来看一下
func testdefer(){defer fmt.Println("calling last")fmt.Println("calling first")
}func main() {testdefer()
}
执行结果
calling first
calling last
可见,defer后面的函数调用在程序最后才去执行。
那如果有多个defer语句怎么办呢?defer执行顺序其实相当于一个defer栈,采用的是先进后出的原则
func testdefer(){defer fmt.Println("calling last")defer fmt.Println("calling next")fmt.Println("calling first")
}func main() {testdefer()
}
执行结果
calling first
calling next
calling last
延迟方法
不仅是函数,方法也可以作为defer的调用
type student struct {name stringage int
}func (s student) print() {fmt.Println(s.name)
}func testdefer(){s := student{"pigff",21}defer s.print()fmt.Println(s.age) //会先执行这句,打印21
}func main() {testdefer()
}
执行结果
21
pigff
defer的实参取值
defer调用的函数内的参数值,并不是在真正调用defer时确定的,而是在执行到defer时就确定了。
还是上面的例子
type student struct {name stringage int
}func (s student) print() {fmt.Println(s.name)
}func testdefer(){s := student{"pigff",21}defer s.print() //执行到defer语句时,name还是pigff,所以在最好defer调用时,也是这个s.name = "Kobe" //这里改变名字,下一句打印的是Kobes.print()
}func main() {testdefer()
}
执行结果
Kobe
pigff
这里在执行到defer语句的时候,s.name还是pigff,所以在最后defer调用时s.name依旧还是pigff,并不是在程序后面改变的Kobe。
再换一个简单的例子
func print(a int) {fmt.Println(a)
}func testdefer(){a := 5defer print(a) //在执行到这一句的时候a还是5a = 10print(10)
}func main() {testdefer()
}
执行结果
10
5
因此,需要注意defer的实参取值,是在执行时确定的,而不是在调用时。
关于defer的实际使用场景
其实在学C++的智能指针的时候,学到过类似的概念。
当我们申请了资源的时候,比如锁,数据库连接,加了锁我们得解锁,建立了连接我们得关闭。假设我们确实写了释放资源的语句,但是如果程序突然在执行释放语句之前return了,比如说报出panic了导致程序中断等,这个时候释放资源的语句就没有被调用了,那么我们申请的资源就会没有释放,长此以往就会导致资源泄漏等很多问题。
虽说在程序运行结束时资源都会全部释放,但是一般服务器程序是不会经常关闭的。
所以defer调用保证调用的函数肯定会在函数结束之前被执行,即使程序报了panic中断,defer调用依旧会被执行。
这就是defer调用的好处,它经常与释放资源等操作配套执行,确保资源被释放。
常用的场景如下(都是配套的,我们在申请资源时就应该使用defer写释放资源的语句)
- 打开文件,关闭文件
- 加锁,解锁
- 建立连接,释放连接
更多推荐
GO学习笔记——defer调用(21)
发布评论