Go 语言的 defer 语句会将其后面跟随的语句进行延迟处理。在 defer 所在的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行。类似栈的访问方式,先进后出。最先被 defer 的语句最后被执行,最后被 defer 的语句最先被执行。
Go 的 defer 的用法 与 Java 中的 finally 非常的相似。一般用来释放资源,比如文件句柄、数据库连接或者互斥锁等等。
defer 特性
- 关键字 defer 用于注册延迟调用
- 这些调用直到 return 前才被执。因此,可以用来做资源清理
- 多个defer语句,按先进后出的方式执行
- defer语句中的变量,在defer声明时就决定了
defer 用途
- 关闭文件句柄
- 锁资源释放
- 数据库连接释放
多个延迟语句的执行顺序
通过上面的介绍我们知道,defer 注册延迟处理函数与栈的访问方式非常的相似。秉承着先进后出的执行顺序。通过下面的程序验证一下。
package main
import "fmt"
func main () {
fmt.Print("defer begin ")
defer fmt.Print(" 1 ")
defer fmt.Print(" 2 ")
defer fmt.Print(" 3 ")
fmt.Print(" defer end ")
}
- 程序输出:defer begin defer end 3 2 1
通过程序输出结果可以看出,defer 后面的语句会在普通语句执行完成后才执行。并且是 defer 的逆序执行的。
defer 释放资源
使用之前的一个例子作为演示。代码 1 处将文件的关闭延迟处理。当函数执行完成即将返回时会将文件资源释放。
package main
import "fmt"
import "os"
func main () {
f, err := os.Open("D:\\tmp\\hello.txt")
if err != nil {
fmt.Printf("打开文件错误,%v", err)
return
}
defer f.Close() // 1
buf := make([]byte, 8)
for n, _ := f.Read(buf); n != 0; n, _ = f.Read(buf) {
fmt.Printf("读取 %d byte 内容,内容:%q\n", n, buf[:n])
}
}
defer 匿名函数
之前的例子都是 defer 延迟处理一个函数,defer 也可以延迟处理匿名函数,具体演示代码如下。
package main
import "fmt"
func main () {
fmt.Print("defer begin ")
defer func () {
fmt.Print(" 1 ")
fmt.Print(" 2 ")
fmt.Print(" 3 ")
}()
fmt.Print(" defer end ")
}
- 程序输出:defer begin defer end 1 2 3