Golang defer函数 延迟执行

defer  函数


关键字 defer 允许我们推迟到函数返回之前(或任意位置执行 return 语句之后)一刻才执行某个语句或函数(为什么要在返回之后才执行这些语句?因为 return 语句同样可以包含一些操作,而不是单纯地返回某个值)。
(译者注:return 是非原子性的,需要两步,执行前首先要得到返回值 (为返回值赋值),return 将返回值返回调用处。

defer 和 return 的执行顺序是先为返回值赋值,然后执行 defer,然后 return 到函数调用处。

关键字 defer 的用法类似于面向对象编程语言 Java 和 C# 的 finally 语句块,它一般用于释放某些已分配的资源。

在函数中,程序员经常需要创建资源(比如:数据库连接、文件句柄、锁等),为了在函数执行完毕后,及时的释放资源,Go的设计者提供defer(延时机制,只要函数执行完毕之后,那么它一定会将你指定的资源关闭)。 

func TestDefer(t *testing.T) {

defer func() { //后面可以是命名函数,或者匿名函数,总之是函数调用
    t.Log("Clear resources")
}()
    t.Log("Started")
    panic("Fatal error”) 
//defer仍会执⾏,如果是其他语句放在Panic之后,那么这行语句是不可达的,
//也就是Panic后面的语句是不可执行的
}

defer的函数调用不会立刻执行,而是会执行Panic语句,执行完了在函数返回前才会执行defer的函数,依据这种特性,可能会想到在java里面的try finally的结构,这个类似于finally,最后返回时候执行的。

这有什么用呢,通常都是清理某些资源,释放某些锁,不管是报了异常还是异常退出了,都不会忘记将我们的锁/资源释放掉,以免导致资源的浪费,导致程序被锁住。

这里抛出了Panic,这里是程序异常中断,在go里面代表不可修复的错误。即便是在Panic的情况下,defer后面的函数也是会执行的。

当代码执行到defer到时候,暂时不会执行,而是会压入到独立defer的栈中,和函数栈不在同一个地方。当函数执行完毕之后再从defer栈中按照先入后出的方式执行(其实就是return过后,或者没有return的函数执行完毕之前,再去defer栈中取数据)。 

defer 关键字用户声明函数,不论函数是否发生错误都在函数执行最后执行(return 之前)若使用 defer 声明多个函数,则按照声明的顺序,先声明后执行(堆) 常用来做资源释放,记录日志等工作。

其实就是将函数执行完毕要执行的语句先压入到defer栈当中,等到函数执行完毕之后依次的执行。defer后面一般是加匿名函数的调用!

fmt.Println("start")
defer func() {
   fmt.Println("defer")
}()
fmt.Println("end")


start
end
defer

defer的作用就是延时执行,defer延时到什么时候呢,延时到函数退出之前执行

fmt.Println("start")

defer func() {
   fmt.Println("defer1")
}()

defer func() {
   fmt.Println("defer2")
}()

fmt.Println("end")

start
end
defer2
defer1

可以看到是堆栈的模式,谁先声明,最后执行。

将defer语句放到栈时,也会将相关的值拷贝入栈。当时的值是多少就入栈是多少,就算变量在后面发生了变化,依然不会有影响!!!!!!!!!!!!!!!!!!!!!

举例


是否可以有多个 defer,如果可以的话,它们的执行顺序是怎么样的?对于这道题,可以直接采用写代码测试的方式,如下所示:

func moreDefer(){
   defer  fmt.Println("First defer")
   defer  fmt.Println("Second defer")
   defer  fmt.Println("Three defer")
   fmt.Println("函数自身代码")
}

func main(){
  moreDefer()
}

我定义了 moreDefer 函数,函数里有三个 defer 语句,然后在 main 函数里调用它。运行这段程序可以看到如下内容输出:

函数自身代码
Three defer
Second defer
First defer

 通过以上示例可以证明:

  1. 在一个方法或者函数中,可以有多个 defer 语句;

  2. 多个 defer 语句的执行顺序依照后进先出的原则。

defer 有一个调用栈,越早定义越靠近栈的底部,越晚定义越靠近栈的顶部,在执行这些 defer 语句的时候,会先从栈顶弹出一个 defer 然后执行它,也就是我们示例中的结果。

defer的最佳实践


defer最主要的价值是在,当函数执行完毕后,可以及时的释放函数到建的资源,看下模拟代码。

1) 在golang编程中的通常做法是,创建资源后,比如(打开了文件,获取了数据库的链接,或者是锁资源), 可以执行defer file.Close() defer connect.Close()

2) 在defer后,可以继续使用创建资源

3) 当函数完毕后,系统会依次从defer栈中,取出语句,关闭资源,这种机制,非常简洁,程序员不用再为在什么时机关闭资源而烦心

defer 函数 


在一个自定义函数中,你打开了一个文件,然后需要关闭它以释放资源。不管你的代码执行了多少分支,是否出现了错误,文件是一定要关闭的,这样才能保证资源的释放。

如果这个事情由开发人员来做,随着业务逻辑的复杂会变得非常麻烦,而且还有可能会忘记关闭。基于这种情况,Go 语言为我们提供了 defer 函数,可以保证文件关闭后一定会被执行,不管你自定义的函数出现异常还是错误。

延时执行有个关键字defer,defer后面跟着的是函数调用函数调用就是要加括号的

下面的代码是 Go 语言标准包 ioutil 中的 ReadFile 函数,它需要打开一个文件,然后通过 defer 关键字确保在 ReadFile 函数执行结束后,f.Close() 方法被执行,这样文件的资源才一定会释放。

func ReadFile(filename string) ([]byte, error) {

   f, err := os.Open(filename)
   if err != nil {
     return nil, err
   }
   defer f.Close()
   //省略无关代码
   return readAll(f, n)
}

defer 关键字用于修饰一个函数或者方法,使得该函数或者方法在返回前才会执行,也就说被延迟,但又可以保证一定会执行。

以上面的 ReadFile 函数为例,被 defer 修饰的 f.Close 方法延迟执行,也就是说会先执行 readAll(f, n),然后在整个 ReadFile 函数 return 之前执行 f.Close 方法。

defer 语句常被用于成对的操作,如文件的打开和关闭,加锁和释放锁,连接的建立和断开等。不管多么复杂的操作,都可以保证资源被正确地释放。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值