延迟函数调用(deferred function call)是golang中很有特点的一个功能,通过defer修饰的函数调用会在函数退出的时候才被真正调用,它可以用来进行资源释放等收尾工作。
一个普通的函数调用被defer关键字修饰以后,就构成了一个延迟函数调用,和协程调用类似,被延迟的函数调用的所有返回值都会全部被舍弃。
延迟函数调用的用法
首先我们看看延迟函数调用的用法,它的用法其实很简单!
简单是示例代码:
func main(){
defer func() {
fmt.Println("延迟调用") }()
fmt.Println("正常调用")
}
代码执行结果:
正常代码
延迟调用
从结果我们可以很直观的发现,被defer修饰的函数调用在后面执行了,也就是被延迟调用了。
多个延迟函数调用的执行顺序
上面的示例代码很简单,输出结果也很好理解,接下来我们看一个稍微复杂一丢丢的代码:
func main(){
defer func() {
fmt.Println("延迟调用1") }()
defer func() {
fmt.Println("延迟调用2") }()
fmt.Println("正常代码")
}
这段代码和之前的代码唯一的不同之处是多了一个被defer修饰的函数调用,那么这小段代码的输出又会是什么样的呢?
是先输出[延迟调用1]还是先输出[延迟调用2]呢?跑一下程序自然就知道啦!
代码执行结果:
正常代码
延迟调用2
延迟调用1
[延迟调用2]被先输出了,这是为什么呢?
这是因为每一个协程都会维护一个延迟调用堆栈,按照代码顺序把需要延迟调用的函数压入栈中,当函数进入退出阶段后,就会从延迟调用堆栈中取出需要执行的函数调用并执行。
延迟函数调用的实现原理
上面讲了基本的用法,接下来我们再深入到golang源码看看defer关键字是如何实现的。为了便于分析,我用下面的代码进行分析:
func main(){
defer func() {
}()
defer func() {<