一个有关Golang Deferred Function 执行顺序的问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一个有关Golang Deferred Function 执行顺序的问题相关的知识,希望对你有一定的参考价值。

先看一下一段关于defer的解释, 引自《Go程序设计语言》

Syntactically, a defer statement is an ordinary function or method call prefixed by the keyword defer.
The function and argument expressions are evaluated when the statement is executed,
but the actual call is deferred until the function that contains the defer statement has finished, whether normally,
by executing a return statement or falling off the end, or abnormally, by panicking.
Any number of calls may be deferred; they are executed in the reverse of the order in which they were deferred.

大意是说:

  从语法上来说,defer 声明就像一个普通函数或方法,只是加了一个关键词为defer的前缀。当defer声明语句被程序执行时,函数和参数表达式被计算,但是直到包含defer声明的函数完成之后才被真正的调用,无论函数的结束方式是正常情况下执行return语句,还是异常情况下执行panic。可以声明多个defer语句,他们的按照声明的倒序执行。

 

吐槽一下自己先前的理解,以为程序执行时遇到defer语句时,跳过去,整个函数执行完毕再从下到上找到defer语句执行 =_= 

很傻很天真,直到遇到下面的问题:

package main

import "fmt"

func main() {
        defer pp(1, f(3))

        defer pp(2, f(4))

}

func pp(index int, r int) {
        fmt.Println("defer", index, ": ", r)
}

func f(index int) int {
        fmt.Println(index)
        return index
}

 

此处sleep 10 秒钟,思考一下打印结果。

 

想象着是:

4

defer 2 :  4

3

defer 1 :  3

 

实际运行结果是:

3
4
defer 2 : 4
defer 1 : 3

 

理想和现实有些差距,原因是想错了!

===========================================================

另一个问题:defer return 的执行顺序

引自《Go程序设计语言》:

Deferred functions run afther return statements have updated the function‘s result variables. 
Because an anonymous function can access its enclosing function‘s variables, including named results, 
a deferred anoymous function can observer the function‘s results.
package main

import "fmt"

func main() {
        fmt.Println("triple(4) =", triple(4))
}

func double(x int) (result int) {
        defer func() { fmt.Printf("double(%d) = %d\n", x, result) }()
        return x + x
}

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

上面代码中triple函数中的defer在return语句执行完成之后,更新了函数的返回值的变量result,所以结果如下:

double(4) = 8
triple(4) = 12

修改一下代码:

[email protected]:~$ vim test.go
package main

import "fmt"

func main() {
        fmt.Println("triple(4) =", triple(4))
}

func double(x int) (result int) {
        defer func() { fmt.Printf("double(%d) = %d\n", x, result) }()
        return x + x
}

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

返回结果:

double(4) = 8

triple(4) = 8

我的理解是,执行return语句后,将result赋值给函数的结果变量,这个变量是匿名的。此时result和结果的匿名变量是两个变量,当defer语句更改result值得时候,结果变量不会改变。因此函数的返回结果仍然为defer执行前的result的值。

再修改一下:

package main

import "fmt"

func main() {
        fmt.Println("triple(4) =", *(triple(4)))
}

func double(x int) (result int) {
        defer func() { fmt.Printf("double(%d) = %d\n", x, result) }()
        return x + x
}

func triple(x int) *int {
        var result int
        result = double(x)
        defer func() { result += x }()
        return &result
}

结果如下:

double(4) = 8
triple(4) = 12

这里利用的指针传递

以上是关于一个有关Golang Deferred Function 执行顺序的问题的主要内容,如果未能解决你的问题,请参考以下文章

写一个 golang 风格的协程扩展

golang bytes.Buffer Reset

在 JavaScript 中,如何在超时中包装承诺?

jQuery的Deferred对象

jquery中的 deferred之 deferred对象

算法基础:删除字符串中出现次数最少的字符(Golang实现)