递延函数只有在包含defer 语句的主函数完成后才会被执行。这包括正常退出、恐慌和异常退出,所以延迟函数总是在最后被执行。延迟函数最常见的使用情况是在主控制流之后进行打开/关闭、连接/断开、锁定/解锁等类型的操作。请看下面的例子。
例子
func main() {
fmt.Println("main: begin")
fmt.Println("main: 1")
defer child()
fmt.Println("main: 2")
fmt.Println("main: end")
}
func child() {
fmt.Println("child: begin")
fmt.Println("child: 1")
fmt.Println("child: end")
}
// main: begin
// main: 1
// main: 2
// main: end
// child: begin
// child: 1
// child: end
如下图所示,所有的延迟函数都会被执行,但无论它们被调用多少次,都是以相反的顺序:
func main() {
defer child(0)
fmt.Println("main: begin")
defer child(1)
fmt.Println("main: middle")
defer child(2)
fmt.Println("main: end")
defer child(3)
}
func child(i int) {
fmt.Printf("child: %d\n", i)
}
// main: begin
// main: middle
// main: end
// child: 3
// child: 2
// child: 1
// child: 0
需要记住的一件事是,如果你需要在一个循环中调用defer ,你必须将相关功能移到一个额外的函数中,包括defer 语句本身。如果你不这样做,你会造成资源 "泄漏",请看下面的区别:
// This is wrong!
func main() {
fmt.Println("main: begin")
for i := 1; i < 4; i++ {
defer child(i) // Resource leak.
}
fmt.Println("main: end")
}
func child(i int) {
fmt.Printf("child: %d\n", i)
}
// main: begin
// total: 8
// main: end
// child: 3
// child: 2
// child: 1
在上面的例子中,推迟的操作将不得不等待循环的完成。因此,它将导致资源 "泄漏 "的原因。然而,下面的例子没有这样的问题,因为,推迟的操作将在每个迭代中立即运行。请注意输出:
// This correct!
func main() {
total := 1
fmt.Println("main: begin")
for i := 1; i < 4; i++ {
total = total * 2
middleman(i)
}
fmt.Printf("total: %d\n", total)
fmt.Println("main: end")
}
func middleman(i int) {
defer child(i)
}
func child(i int) {
fmt.Printf("child: %d\n", i)
}
// main: begin
// child: 1
// child: 2
// child: 3
// total: 8
// main: end