关于延时调用函数(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 的过程,直接赋值返回值

Go-延时函数defer的更多相关文章

  1. Golang入门教程(十三)延迟函数defer详解

    前言 大家都知道go语言的defer功能很强大,对于资源管理非常方便,但是如果没用好,也会有陷阱哦.Go 语言中延迟函数 defer 充当着 try...catch 的重任,使用起来也非常简便,然而在 ...

  2. Android 如何通过代码模拟按钮点击 延时函数

    View.performClick();   比如: private Button mButton01;    mButton01 = (Button)findViewById(R.id.myButt ...

  3. 延时函数出错,volatile一例

    莫名其妙的错误.使用Systick做的延时. 初始化是这样的: //SysTick配置 SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); if ...

  4. JavaScript的sleep延时函数

    JavaScript没有像Java的sleep延时函数,所以记录JavaScript的sleep延时函数 function sleep(milliSeconds) { var startTime = ...

  5. C# 非独占延时函数 非Sleep

    在C#窗口程序中,如果在主线程里调用Sleep,在Sleep完成之前, 界面呈现出假死状态,不能响应任何操作! 下边实现的是非独占性延时函数,延时过时中界面仍可响应消息: public static ...

  6. Qt自定义sleep延时函数(巧妙的使用时间差,但这样似乎CPU满格,而不是沉睡)

    Qt不像VC++的win32/MFC编程那样,提供了现成的sleep函数可供调用.Qt把sleep函数封装在QThread类中.子线程可以调用sleep函数.但是如果用户想在主线程实现延时功能,该怎么 ...

  7. 改进的延时函数Delay(使用MsgWaitForMultipleObjects等待消息或超时的到来)

    解决上一节中延时函数占CPU使用率(达50%)的第二种方法是利用消息机制,通过API函数MsgWaitForMultipleObjects等待消息或超时的到来,从而避免使用循环检测使CPU占用率过高. ...

  8. stm32中的延时函数

    //粗延时函数,微秒 void delay_nus(u16 time) { u16 i=0; while(time--) { i=10;  //自己定义 while(i--) ; } } //毫秒级的 ...

  9. QT 延时函数设置

    QT 的延时函数分为非阻塞延时 和 阻塞型延时 非阻塞延时: void GreenPass3::delaymsec(int msec){    QTime dieTime = QTime::curre ...

  10. Go 延迟函数 defer 详解

    Go 延迟函数 defer 详解 Go 语言中延迟函数 defer 充当着 try...catch 的重任,使用起来也非常简便,然而在实际应用中,很多 gopher 并没有真正搞明白 defer.re ...

随机推荐

  1. es5原型式继承间解

    1. 原型式继承方法 js 继承使用不难,要说清楚,需要自己一定总结,才能说清楚. es5 的继承方式有很多种,这个是 js 语言本身造成,但是类实现继承之后的功能,有如下 3 条: 子类继承父类,主 ...

  2. gradle入门教程

    1,https://gradle.org/ 下载过后解压缩,绿色软件不需要安装. 配置系统环境:GRADLE_HOME设置为解压缩之后的地址,PATH属性追加%GRADLE_HOME%\bin; 2, ...

  3. How to use draggable attribute?怎样使用拖拽属性代码分享

    6.7 Drag and dropSupport: dragndropChrome for Android NoneChrome 4+iOS Safari 11.0+UC Browser for An ...

  4. Failed to set MokListRT: Invalid Parameter Something as gone seriously wrong: import_mok_state() failed: Invalid Parameter

    今天yum update升级centos7,重启后发现开不了机,报错如下: Failed to set MokListRT: Invalid ParameterSomething as gone se ...

  5. 干货 | LIDAR、ToF相机、双目相机如何科学选择?

    点击"计算机视觉life"关注,置顶更快接收消息! 本文阅读时间约5分钟 本文翻译自卡内基梅隆大学 Chris asteroid 三维视觉技术的选择 传感器参数及定义 LIDAR ...

  6. 把玩Pencil项目之编译并运行

    Pencil是个好项目.使用Electron作为运行环境,如同Vs Code一样,将JS跨平台桌面应用做了一个好的示范.个人很喜欢这种方式,毕竟多年来关注Web全栈开发,有一种JS一统天下的感觉.我的 ...

  7. windows10误删Administrator用户的家目录之后

    今天在玩Windows10的用户管理的时候,把Administrator用户给开启了,然后还用这个用户登录了系统. 结果就是,第一次登录的时候,闪过一条条初始化配置欢迎信息,Windows系统为Adm ...

  8. 解决c# progressBar更新出现界面假死

    最近一个项目需求中的一个功能是需要用progressBar反映处理文件的进度. 研究了Invoke和BeginInvoke方法. Control.Invoke 方法 (Delegate) :在拥有此控 ...

  9. CentOS7.3安装zimbra8.7.11

    系统版本:CentOS7.3 Zimbra版本:8.7.11 设置IP地址 vi /etc/sysconfig/network-scripts/ifcfg-ens33 #使用vi命令修改ip BOOT ...

  10. Delphi调用API函数获取Windows目录信息、获取System目录信息、获取Temp临时文件目录信息

    var Str1, Str2: Array[..Max_Path]of Char;//开辟缓冲区 Str3: Array[..]of Char; begin GetWindowsDirectory(@ ...