zoukankan      html  css  js  c++  java
  • Go-延时函数defer

    关于延时调用函数(Deferred Function Calls)

         延时调用函数基本语法如下:

    defer func_name(param-list) {}
    

      当一个函数前有关键字 defer 时,那么这个函数执行会被推迟到包含这个 defer 语句的函数即将返回前才执行,

    如下示例:

    package main
    
    import "fmt"
    
    func main() {
    	defer fmt.Println("Fourth")
    	fmt.Println("First")
    	fmt.Println("Third")
    }
    

      运行打印输出结果:

    First
    Third
    Fourth
    

     需要注意的是,defer 调用的函数参数,在定义 defer 时就已经被确定了,如下示例:

    package main
    
    import "fmt"
    
    func main() {
    	i := 1
    	defer fmt.Println("Deferred print:",i)
    
    	i++
    	fmt.Println("Normal print:",i)
    }
    

      运行打印输出结果:

    Normal print: 2
    Deferred print: 1
    

      从上面的结果中我们可以知道,在 defer fmt.Println("Deferred print:"i,) 调用时,i 的值就已经被确定了,因此相当

    于 defer fmt.Println("Deferred print:",1)

        需要强调的是,defer 调用的函数的参数值在 defer 定义时就已经被确定了,而 defer 函数内部所使用的变量的值需

    要在运行时才确定。如下示例:

    package main
    
    import "fmt"
    
    func f1() (r1 int) {
    	r := 1
    	defer func() {
    		r ++
    		fmt.Println(r)
    	}()
    
    	r = 2
    	return
    }
    
    func main() {
    	f1()
    }
    

      运行打印输出结果:

    3
    

      上面的例子中,我们看到最终打印的内容是 "3" ,这是因为在 "r = 2" 之后才执行的 defer 函数,因此在这个函数内,

    r 的值是 2,自增后变成 3

    defer 顺序

           如果有多个 defer 调用,则调用的顺序是先进后出的顺序,类似于入栈出栈操作一样:

    package main
    
    import "fmt"
    
    func main() {
    	defer fmt.Println(1)
    	defer fmt.Println(2)
    	defer fmt.Println(3)
    	defer fmt.Println(4)
    }
    

      运行打印输出结果:

    4
    3
    2
    1
    

    defer 注意要点

           defer 函数调用的执行时机是外层函数设置返回值之后,即将返回之前

           如下示例:

    package main
    
    import "fmt"
    
    func f1() (r int) {
    	defer func() {
    		r ++
    	}()
    
    	return 0
    }
    
    func main() {
    	fmt.Println(f1())
    }
    

      运行打印输出结果:

    1
    

      上面 fmt.Println(f1()) 打印的结果是 1,要弄明白这个问题,我们需要牢记两点:

           1. defer 函数调用的执行时机是外层函数设置返回值之后,即将返回之前

           2.return xxx 操作并不是原子

           我们将上面的例子改写一下:

    func f1() (r int) {
    	defer func() {
    		r ++
    	}()
    
    	r = 0
    	return
    }
    

      当进行赋值操作 r = 0 之后,才执行调用 defer 函数,最后才执行返回语句

           下面我们再看一个例子:

    package main
    
    import "fmt"
    
    func double(x int) int  {
    	return x + x
    }
    
    func triple(x int) (r int)  {
    	defer func() {
    		r += x
    	}()
    
    	return double(x)
    }
    
    func main() {
    	fmt.Println(triple(3))
    }
    

      上面的代码根据我们的讲解,可以改写成如下代码:

    func triple(x int) (r int)  {
    	r = double(x)
    	defer func() {
    		r += x
    	}()
    	
    	return
    }

      

    ---------------------------------------------------------------------2019/03/04------------------------------------------------------

    转载地址:https://juejin.im/post/5b9b4acde51d450e5071d51f

    参考文章:https://my.oschina.net/henrylee2cn/blog/505535

    1:defer 在匿名返回值和有名返回值函数中的不同表现

         先看下面两个方法执行的结果

    func returnValues() int {
        var result int
        defer func() {
            result++
            fmt.Println("defer")
        }()
        return result
    }
    
    func namedReturnValues() (result int) {
        defer func() {
            result++
            fmt.Println("defer")
        }()
        return result
    }
    

      上面的方法输出 0,下面的方法输出 1。上面的方法使用了匿名返回值,下面的方法使用了有名返回值,除此之外

    逻辑都一样,但是为什么输出的结果的不一样呢?

         1 多个 defer 的执行顺序为"先进后出"

         2 所有函数在执行 return 返回指令之前,都会检查是否存在 defer 语句,如果存在将逆序调用 defer 语句执行完以后

    在退出返回

         3 有名返回值在函数声明的时候就已经被声明了,另外,return 不是一个原子操作,它包含前后两个步骤,第一步是:

    给返回值赋值(如果是有名返回值则直接赋值,如果是匿名返回值则先声明再赋值);第二步是检查是否存在 defer 存在就

    执行 defer, 最后结束函数运行

          以匿名返回值为例,过程如下:

          1. 因为匿名返回值并没有明确声明返回值,可以理解成 Go 自动创建声明了一个返回值 retValue ,然后将 result 赋值给

    返回值 retValue 

           2.然后检查是否有 defer ,如果有则返回

           3. 返回刚才创建的返回值

           在这种情况下,defer 中的修改是对 result 执行的,而不是 retValue ,在有名返回值中,返回值已经在函数定义的时候就

    已经被声明了,没有创建声明 retValue 的过程,直接赋值返回值

  • 相关阅读:
    基于docker安装pxc集群
    PXC集群的概述及搭建
    十,StatefulSet简介及简单使用
    九,configMap及secret的基本使用
    八,kubernetes集群存储卷基础。
    七,ingress及ingress cluster
    六,k8s集群service资源
    mysql的优化
    ORACLE11g:No Dialect mapping for JDBC type: -9解决方案
    Oracle数据库的分页
  • 原文地址:https://www.cnblogs.com/leeyongbard/p/10417704.html
Copyright © 2011-2022 走看看