一个有关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

这里利用的指针传递

时间: 2024-10-11 15:58:52

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

UIViewController的生命周期及iOS程序执行顺序

当一个视图控制器被创建,并在屏幕上显示的时候. 代码的执行顺序1. alloc                                   创建对象,分配空间2.init (initWithNibName) 初始化对象,初始化数据3.loadView                          从nib载入视图 ,通常这一步不需要去干涉.除非你没有使用xib文件创建视图4.viewDidLoad                   载入完成,可以进行自定义数据以及动态创建其他控件5

[Unity-20] Unity不同脚本之间的执行顺序

我们都知道,Unity中某个脚本的执行顺序是Awake.Start.Update.LateUpdate等,但是不同的脚本之间的执行顺序是怎样的呢? 例如我有两个脚本Script1和Script2,那么这两个脚本的Awake执行顺序是怎样的呢? 要知道真相我们就必须了解Unity后台的运行原理,其实Unity后台是单线程执行的,所谓的协程都是伪多线程.不同脚本的Awake在后台的执行真相是这样的: 后台Awake() { 脚本0Awake(); 脚本1Awake(); ........ } Sta

Unity3d脚本执行顺序详解

欢迎来到unity学习.unity培训.unity企业培训教育专区,这里有很多U3D资源.U3D培训视频.U3D教程.U3D常见问题.U3D项目源码,我们致力于打造业内unity3d培训.学习第一品牌. 在调用脚本的时候遇到下面报错情况: NullReferenceException: Object reference not set to an instance of an object 意思就是:未将对象引用设置到对象的实例脚本内部的 Start()函数并不能作为构造的形式,因为在同一个对象

SQL语句的执行顺序--知道执行顺序,顺便可以做优化了,对吧

今天,被面试问到了一个问题,SQL的执行顺序,当时想的是,SQL有啥执行顺序,从上往下?当时瞎胡邹,说先执行from,为啥,不执行from怎么知道操作查询的是什么表,对吧,哎嘿,还蒙对了.下来之后总结了一下 (一)先讲讲查询吧 查询语句中select from where group by having order by的执行顺序 1.查询中用到的关键词主要包含六个,并且他们的顺序依次为 select--from--where--group by--having--order by 其中sele

junit用法,before,beforeClass,after, afterClass的执行顺序

JUnit4使用Java5中的注解(annotation),以下是JUnit4常用的几个annotation: @Before:初始化方法 对于每一个测试方法都要执行一次(注意与BeforeClass区别,后者是对于所有方法执行一次) @After:释放资源 对于每一个测试方法都要执行一次(注意与AfterClass区别,后者是对于所有方法执行一次) @Test:测试方法,在这里可以测试期望异常和超时时间 @Test(expected=ArithmeticException.class)检查被测

验证多个Filter过滤一个资源时执行顺序

当有多个Filter过滤一个资源时,执行顺序到底是怎样? 如下测试: 定义两个Filter,FirstFilter,SecondFilter,它们过滤同一个index.jsp页面,并且SecondFilter的映射写在FirstFilter后面 并在两个Filter的doFilter()方法中加上输出语句: 例如FirstFilter的doFilter方法: @Override public void doFilter(ServletRequest request, ServletRespons

autofac 一个接口多个实现的顺序执行

接口: namespace AutofacTest.Interface { public interface IUserInfo { string GetUserINfo(int uid); int sort(); } } 接口的实现: public class CrmUserInfoHelper : IUserInfo { public string GetUserINfo(int uid) { return "crmuserinfo"; } public int sort() {

Oracle一个事务中的Insert和Update执行顺序

今天碰到了一个奇怪的问题,是关于Oracle一个事务中的Insert和Update语句的执行顺序的问题. 首先详细说明下整个过程: 有三张表:A,B,C,Java代码中有一段代码是先在表A中插入一条数据,然后再更新表B的两个字段,更新的两个字段是特定值.并且插入和更新在一个事务中. 有个需求需要在表A添加一个Insert的行级触发器,在触发器里,插入表A一行记录后去表B查看更新的两个字段是否满足特定条件, 如果表B的两个字段同时等于特定值,则把表A和表B的数据整合下放到表C.触发器的初衷就是这样

关于Ajax load页面中js部分$(function(){})的执行顺序

<script type="text/javascript"> console.error(11111); $(function(){ console.error(22222); }); console.error(33333); </script> 在一般页面的直接加载中,上面这段代码的执行顺序: 不过,在使用Ajax加载这个页面到某个div中时,执行顺序发生改变: 看来使用Ajax的时候,需要注意这个js的执行顺序.