Go延迟调用
defer
是go
中一种延迟调用机制,defer
后面的函数只有在当前函数执行完毕后才能执行,将延迟的语句按defer的逆序进行执行,也就是说先被defer
的语句最后被执行,最后被defer
的语句,最先被执行,通常用于释放资源。
go
defer function([parameter_list]) // 延迟执行函数
defer method([parameter_list]) // 延迟执行方法
defer语句的执行顺序
多个defer出现的时候,它会把defer之后的函数压入一个栈中延迟执行,也就是先进后出(LIFO),写在前面的defer会比写在后面的defer调用的晚。下面通过一个示例看一下:
go
func func1(){
fmt.Println("我是 func1")
}
func func2(){
fmt.Println("我是 func2")
}
func func3(){
fmt.Println("我是 func3")
}
func main(){
defer func1()
defer func2()
defer func3()
fmt.Println("main1")
fmt.Println("main2")
}
执行输出如下:
main1
main2
我是 func3
我是 func2
我是 func1
延迟函数的参数
defer后面的函数在入栈的时候保存的是入栈那一刻的值,会将变量的实际值传入栈中,而不会在后期根据变量改变而改变。
go
func main(){
i:= 0
defer func(a int) {
fmt.Println(a)
}(i)
i++
}
此时输出的值是0,而不是1,因为defer后面的函数在入栈的时候保存的是入栈那一刻的值,而当时i的值是0,所以后期对i进行修改,并不会影响栈内函数的值。
如果我们把参数传引用
go
func main(){
i:= 0
defer func(a *int) {
fmt.Println(*a)
}(&i)
i++
}
此时输出的值是1,因为这里defer后面函数入栈的时候唇乳的执行变量i的指针,后期i值改变的时候,输出结果也会改变。
延迟函数进阶
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()
}
}
这是一个初学者常犯的错误.
错误结果1:
a closed
b closed
c closed
没有考虑defer的LIFO特性,简单的认为和return类似导致的错误.
错误结果2:
c closed
b closed
a closed
考虑了defer的特性.认为在循环调用时,每次会在循环的时候调用defer语句,最终输出c b a.
正确结果
c closed
c closed
c closed
在运行中,栈中存入了三个defer t.close()语句,而t在结束的时候是指向同一个变量t,其值为c,所以输出了ccc.
go
package main
import "fmt"
func foo() (i int) {
i = 0
defer func() {
fmt.Println(i)
}()
return 2
}
func main() {
foo()
}
- 函数声明了一个命名返回值
i
。 - 函数内部有一个
defer
语句,它延迟执行一个匿名函数,这个匿名函数打印出i
的值。 - 函数通过
return 2
结束,这实际上将i
设置为2
。 - 在函数返回之前,
defer
语句执行。此时,匿名函数中打印的是命名返回值i
的当前值,也就是2
。