Go语言defer语句的用法
在进行 I/O 操作时,如果遇到错误,需要提前返回,而返回之前应该关闭相应的资源,否则容易造成资源泄露等问题,这时就需要使用 defer 语句来解决这些问题。
使用 defer 语句打开资源,不仅减少了代码的书写量,而且使程序变得更加简洁,例如:
也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行。
例如:
使用 defer 语句打开资源,不仅减少了代码的书写量,而且使程序变得更加简洁,例如:
func ReadWrite() bool{ file.Open("file") defer file.Close() //打开和关闭写在一块,方便管理 if aFailure { return false } else if bFailure { return false } return false }在 defer 后指定的函数会在函数退出前调用。如果多次调用 defer,那么 defer 采用后进先出的模式,例如:
package main import ( "fmt" ) func main() { for i := 0; i < 5; i++ { defer fmt.Printf("%d\n", i) } }运行结果为:
4
3
2
1
0
- defer 后面必须是函数或方法的调用,不能是语句,否则编译器会提示“expression in defer must be function call”错误;
- defer 函数的实参在注册时通过值复制传递;
- defer 语句必须先注册后才能执行,如果 defer 位于 return 之后,则 defer 因为没有注册,不会执行;
- 在主动调用 os.Exit(int) 退出进程时,defer 将不再被执行;
- defer 的好处是可以在一定程度上避免资源泄露,特别是在有很多 return 语句,有多个资源需要关闭的场景中,很容易漏掉资源的关闭操作;
- 使用 defer 改写后,在打开资源无报错后直接调用 defer 关闭资源,养成这样的编程习惯后,就很难忘记资源的释放;
- defer 语句的位置不当有可能导致 panic,一般 defer 语句放在错误检查语句之后;
- defer 也有明显的副作用:defer 会推迟资源的释放,defer 尽量不要放到循环语句中,而应将大函数内部的 defer 语句单独拆分成一个小函数;
- defer 中最好不要对有名的返回值参数进行操作,否则也会出现错误;
多个defer的执行顺序
多个 defer 语句的执行顺序为“逆序”,defer、return、返回值三者的执行逻辑如下:- defer 最先执行一些收尾工作;
- 然后 return 执行,return 负责将结果写入返回值中;
- 最后函数携带当前返回值退出。
也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行。
例如:
package main import ( "fmt" ) func main() { fmt.Println("defer begin") //将defer放入延迟调用栈 defer fmt.Println(0) defer fmt.Println(1) //最后一个放入, 位于栈顶, 最先调用 defer fmt.Println(2) fmt.Println("defer end") }运行结果为:
defer begin
defer end
2
1
0
- 代码的延迟顺序与最终的执行顺序是反向的;
- 延迟调用在 defer 所在的函数结束时进行,函数结束可以是正常返回时,也可以是发生宕机时。