关于延时调用函数(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. numpy交换列

    x = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) print(x) x = x[:, [1, 0, 2]] print(x) 输出 [[1 2 3] [4 ...

  2. 未能执行URL

    在 <compilation debug="true"> 下 加入: <buildProviders> <add extension=".h ...

  3. mysql中的concat,concat_ws(),group_concat()

    mysql中的concat,concat_ws(),group_concat() 说明: 本文中使用的例子均在下面的数据库表tt2下执行:     一.concat()函数 1.功能:将多个字符串连接 ...

  4. 异步请求取得json数据

    一.异步请求 在之前我们请求数据的时候都是整个页面全部刷新了一次,也就是每次请求都会重新请求所有的资源.但是在很多时候不需要页面全部刷新,仅仅是需要页面的局部数据刷新即可,此时需要发送异步请求来实现这 ...

  5. RN启动报错,环境相关问题

    启动RN的时候刚开始报错: The request was denied by service delegate (SBMainWorkspace) for reason: Security (&qu ...

  6. 3.2.3 SpringMVC注解式开发

    SpringMVC注解式开发 1. 搭建环境 (1) 后端控制器无需实现接口 , 添加相应注解 Controller类添加注解 @Controller //该注解表将当前类交给spring容器管理 @ ...

  7. 浅析对spring中IOC的理解

    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...

  8. msbuild 编译指定工程时构建脚本的配置

    有时候 ,我们编译windows的exe时,我们不需要编译所以的工程,我们只需要指定某个工程就好了,此时我们使用/t:工程名:Rebuild(如果要编译全部工程就把工程名去掉,即/t:Rebuild) ...

  9. 打开eclipse "Initializing Java Tooling"错误

    问题:打开eclipse初始化界面过程中弹出An internal error occurred during: "Initializing Java Tooling". java ...

  10. newcode网学习笔记(1)

    1.类的内存结构一开始指向的是虚函数表,指向虚函数表的指针为4个字节(32位系统)[类的内存结构最前面的是指向虚函数表的指针] 2.包含标准库文件以及相关命名空间,则系统不允许用户重新定义标准库函数, ...