Golang之defer 延迟调用

前言

defer语句被用于预定对一个函数的调用。我们把这类被defer语句调用的函数称为延迟函数。而defer 延迟语句在其他编程语言里好像没有见到。应该是属于 Go 语言里的独有的关键字。但用法类似于面向对象编程语言 Java 和 C# 的 finally 语句块。下面对defer进行介绍。

defer特性

1. 关键字 defer 用于注册延迟调用。
2. 这些调用直到 return 前才被执。因此,可以用来做资源清理。
3. 多个defer语句,按先进后出的方式执行。

1.延迟调用

用法很简单,只需要在函数前面加上 defer就行,就能实现将这个 该函数的调用延迟到当前函数执行完后再执行。例如:

package main 

import (
	"fmt"
)

func myFunc(){
	fmt.Println("minger")

}

func main(){

	defer myFunc()  //等价于defer fmt.Println("minger")
	fmt.Println("程序猿编码")

}

编译运行:
在这里插入图片描述
2.defer 与 return 孰先孰后

defer 和 return 到底是哪个先调用?先看看例子:

package main 

import (
	"fmt"
)

var name string = "go"

func myFunc() string {
    defer func() {
        name = "python"
    }()

    fmt.Println("myFunc 函数里的name:", name)
    return name
}

func main() {
    myName := myFunc()
    fmt.Println("main 函数里的name: ", name)
    fmt.Println("main 函数里的myname: ", myName )

编译运行:

在这里插入图片描述
来看看打印信息,第一行输出,name 此时还是全局变量,值还是go

第二行输出,在 defer 里改变了全局变量,此时name的值已经变成了 python

重点在第三行,为什么输出的是 go ?

解释只有一个,那就是 defer 是return 后才调用的。所以在执行 defer 前,myName 已经被赋值成 go 了。

3.多个defer 逆序执行

还是老规矩先来上代码,看看输出信息,例子:

package main 

import (
	"fmt"
)

func main(){

	name := "go"
	defer fmt.Println(name)

	name = "C/C++"
	defer fmt.Println(name)

	name = "Python"
	fmt.Println(name)

}

编译输出:

在这里插入图片描述

可见 多个defer 是它们会以逆序执行(类似栈,即后进先出)。

defer官方的解释

Each time a “defer” statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked. Instead, deferred functions are invoked immediately before the surrounding function returns, in the reverse order they were deferred. If a deferred function value evaluates to nil, execution panics when the function is invoked, not when the “defer” statement is executed.

翻译一下:

每次defer语句执行的时候,会把函数“压栈”,函数参数会被拷贝下来;当外层函数(非代码块,如一个for循环)退出时,defer函数按照定义的逆序执行;如果defer执行的函数为nil, 那么会在最终调用函数的产生panic.

为什么需要defer?

往往我们在编程的时候,经常需要打开一些资源,比如数据库连接、文件、锁等,这些资源需要在用完之后释放掉,否则会造成内存泄漏。

因此我们有时会忘记关闭这些资源。Golang直接在语言层面提供defer关键字,在打开资源语句的下一行,就可以直接用defer语句来注册函数结束后执行关闭资源的操作。

defer用途

1. 关闭文件句柄
2. 锁资源释放
3. 数据库连接释放

defer的使用其实非常简单,来看看一个简单用途:

package main

import (
	"log"
	"os"
)

func main() {
	f, err := os.OpenFile("text.txt", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0666) //文件没有就创建,文件存在就追加
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	f.WriteString("程序猿编码\n")

}

编译输出:
在这里插入图片描述
在打开文件的语句附近,用defer语句关闭文件。这样,在函数结束之前,会自动执行defer后面的语句来关闭文件。

当然,defer会有小小地延迟,对时间要求特别特别特别高的程序,可以避免使用它。

总结

defer 语句经常使用于成对的操作,比如打开和关闭,连接和断开,加锁和解锁,即便是再复杂的控制流,资源在任何情况下都能够正确释放。

参考:
https://juejin.im/post/5c637a35e51d45783211f387
Go程序设计语言

在这里插入图片描述

欢迎关注公众号【程序猿编码】,添加本人微信号(17865354792),回复:领取学习资料,网盘资料有如下:

在这里插入图片描述

发布了131 篇原创文章 · 获赞 115 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/chen1415886044/article/details/105022265