zoukankan      html  css  js  c++  java
  • [Go] defer 语句

    Go 还有一些特有的流程控制语句,其中一个就是 defer 语句。该语句用于延迟调用指定的函数,它只能出现在函数的内部,由 defer 关键字以及针对某个函数的调用表达式组成。这里被调用的函数称为 延迟函数。一个简单的例子如下:

    func outerFunc()  {
    	defer fmt.Println("函数执行结束前一刻才会被打印")
    	fmt.Println("第一个被打印")
    }

    其中,defer 关键字后面是针对 fmt.Println 函数的调用表达式。代码里也说明了延迟函数的执行时机。这里的 outerFunc 称为 外围函数,调用 outerFunc 的那个函数称为 调用函数。下面是具体的规则:

    1. 当 外围函数 中的语句正常执行完毕时,只有其中所有的 延迟函数 都执行完毕,外围函数 才会真正结束执行。
    2. 当执行 外围函数 中的 return 语句时,只有其中所有的 延迟函数 都执行完毕后,外围函数 才会真正返回。
    3. 当 外围函数 中的代码引发运行时恐慌时,只有其中所有的 延迟函数 都执行完毕后,该运行时恐慌才会真正被扩散至调用函数。

    正因为 defer 语句有这样的特性,所有它成为了执行 释放资源 或 异常处理 等收尾任务的首选。明显的优势有 2 个,如下:

    1. 对 延迟函数 的调用总会在 外围函数 执行结束前执行。
    2. defer 语句在 外围函数 的函数体中的位置不限,并且数量不限。

    不过,使用 defer 语句还有 3 点需要注意:

    第一点:如果在 延迟函数 中使用外部变量,就应该通过参数传入,示例如下:

    func printNumbers()  {
    	for i := 0; i < 5; i++ {
    		defer func(){
    			fmt.Printf("%d", i)
    		}()
    	}
    }

    上述代码的执行结果为 55555,这正是由 延迟函数 的执行时机引起的。待那 5 个延迟函数执行时,它们使用的 i 值已经是 5 了。正确的做法是这样:

    func printNumbers()  {
    	for i := 0; i < 5; i++ {
    		defer func(n int){
    			fmt.Printf("%d", n)
    		}(i)
    	}
    }

    请注意,这时 延迟函数 有了参数,并且在调用它时也传入了参数值。如此一来,打印内容就会是 43210。为什么不是 01234 呢?请看下面的规则。

    第二点:同一个 外围函数 内多个 延迟函数 调用的执行顺序,会与其所属的 defer 语句的执行顺序 完全相反。你可以想象一下,同一个 外围函数 中每个 defer 语句在执行的时候,针对其 延迟函数 的调用表达式都会被压入同一个栈。在该 外围函数 执行结束的前一刻,Go 会从这个堆栈中依次取出并执行。

    第三点:延迟函数 调用若有参数传入,那么那些参数的值会在当前 defer 语句执行时求出。请看下面的示例:

    func printNumbers() {
    	for i := 0; i < 5; i++ {
    		defer func(n int) {
    			fmt.Printf("%d", n)
    		}(i * 2)
    	}
    }

    此时的执行结果是 86420。

    摘自:《Go 并发编程实战(第二版) . 郝林》

  • 相关阅读:
    java中的 equals 与 ==
    String类的内存分配
    SVN用命令行更换本地副本IP地址
    npoi 设置单元格格式
    net core 微服务框架 Viper 调用链路追踪
    打不死的小强 .net core 微服务 快速开发框架 Viper 限流
    net core 微服务 快速开发框架 Viper 初体验20201017
    Anno 框架 增加缓存、限流策略、事件总线、支持 thrift grpc 作为底层传输
    net core 微服务 快速开发框架
    Viper 微服务框架 编写一个hello world 插件02
  • 原文地址:https://www.cnblogs.com/52php/p/6714442.html
Copyright © 2011-2022 走看看