golang 中多个 defer 的执行顺序
引用 Ture Go 中的一个示例:
package main
import "fmt"
func main() {
fmt.Println("counting")
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}
程序执行结果为:
counting
done
9
8
7
6
5
4
3
2
1
0
从结果可以看出,defer
的执行可以看做是一个 FILO
(First In Last Out) 栈。
在编写程序时,如果遇到下面的执行流程,id1
先获取资源,然后 id2
通过 id1
获取,而 id2
的释放 Close
必须要在 id1
之前,如下:
func fun() {
id1 := Open()
...
id2 := id1.Open()
...
id2.Close()
id1.Close()
}
如果使用defer
,其执行顺序和上面完全相同的,所以我们通常在 Open
打开资源后,立即使用 defer Close
,不会引起释放顺序问题。
func fun() {
id1 := Open()
defer id1.Close()
...
id2 := id1.Open()
id2.Close()
...
}
defer 压入栈的是值,如果为函数,则可以修改变量值
func c() (i int) {
defer func() { i++ }()
return 1
}
如上代码,压入栈的是一个函数地址,函数执行完后,执行i++
,会改变返回值,函数返回值为 2。
从上面的通过defer
修改返回值,defer
也可以用于控制恢复panic
断言。
package main
import "fmt"
func main() {
f()
fmt.Println("Returned normally from f.")
}
func f() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered in f", r)
}
}()
fmt.Println("Calling g.")
g(0)
fmt.Println("Returned normally from g.")
}
func g(i int) {
if i > 3 {
fmt.Println("Panicking!")
panic(fmt.Sprintf("%v", i))
}
defer fmt.Println("Defer in g", i)
fmt.Println("Printing in g", i)
g(i + 1)
}
执行结果为:
Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.