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 的过程,直接赋值返回值