[Go] defer 语句
Go 还有一些特有的流程控制语句,其中一个就是 defer 语句。该语句用于延迟调用指定的函数,它只能出现在函数的内部,由 defer 关键字以及针对某个函数的调用表达式组成。这里被调用的函数称为 延迟函数。一个简单的例子如下:
func outerFunc() {
defer fmt.Println("函数执行结束前一刻才会被打印")
fmt.Println("第一个被打印")
}
其中,defer 关键字后面是针对 fmt.Println 函数的调用表达式。代码里也说明了延迟函数的执行时机。这里的 outerFunc 称为 外围函数,调用 outerFunc 的那个函数称为 调用函数。下面是具体的规则:
- 当 外围函数 中的语句正常执行完毕时,只有其中所有的 延迟函数 都执行完毕,外围函数 才会真正结束执行。
- 当执行 外围函数 中的 return 语句时,只有其中所有的 延迟函数 都执行完毕后,外围函数 才会真正返回。
- 当 外围函数 中的代码引发运行时恐慌时,只有其中所有的 延迟函数 都执行完毕后,该运行时恐慌才会真正被扩散至调用函数。
正因为 defer 语句有这样的特性,所有它成为了执行 释放资源 或 异常处理 等收尾任务的首选。明显的优势有 2 个,如下:
- 对 延迟函数 的调用总会在 外围函数 执行结束前执行。
- defer 语句在 外围函数 的函数体中的位置不限,并且数量不限。
不过,使用 defer 语句还有 3 点需要注意:
第一点:如果在 延迟函数 中使用外部变量,就应该通过参数传入,示例如下:
func printNumbers() {
for i := 0; i < 5; i++ {
defer func(){
fmt.Printf("%d", i)
}()
}
}
上述代码的执行结果为 55555,这正是由 延迟函数 的执行时机引起的。待那 5 个延迟函数执行时,它们使用的 i 值已经是 5 了。正确的做法是这样:
func printNumbers() {
for i := 0; i < 5; i++ {
defer func(n int){
fmt.Printf("%d", n)
}(i)
}
}
请注意,这时 延迟函数 有了参数,并且在调用它时也传入了参数值。如此一来,打印内容就会是 43210。为什么不是 01234 呢?请看下面的规则。
第二点:同一个 外围函数 内多个 延迟函数 调用的执行顺序,会与其所属的 defer 语句的执行顺序 完全相反。你可以想象一下,同一个 外围函数 中每个 defer 语句在执行的时候,针对其 延迟函数 的调用表达式都会被压入同一个栈。在该 外围函数 执行结束的前一刻,Go 会从这个堆栈中依次取出并执行。
第三点:延迟函数 调用若有参数传入,那么那些参数的值会在当前 defer 语句执行时求出。请看下面的示例:
func printNumbers() {
for i := 0; i < 5; i++ {
defer func(n int) {
fmt.Printf("%d", n)
}(i * 2)
}
}
此时的执行结果是 86420。
摘自:《Go 并发编程实战(第二版) . 郝林》
[Go] defer 语句的更多相关文章
- go语言之goto语句和函数和defer语句
1.goto关键字 import "fmt" func main() { for i := 0;i <11;i++{ if i == 2{ //关键字,goto跳转到某个位置 ...
- golang学习 ---defer语句
golang语言defer特性详解 defer语句是go语言提供的一种用于注册延迟调用的机制,它可以让函数在当前函数执行完毕后执行,是go语言中一种很有用的特性.由于它使用起来简单又方便,所以深得go ...
- go语言的defer语句
转: https://www.jianshu.com/p/5b0b36f398a2 ---------------------------------------------------------- ...
- Go语言中defer语句使用小结
defer是Go语言中的延迟执行语句,用来添加函数结束时执行的代码,常用于释放某些已分配的资源.关闭数据库连接.断开socket连接.解锁一个加锁的资源.Go语言机制担保一定会执行defer语句中的代 ...
- 探究 Go 语言 defer 语句的三种机制
Golang 的 1.13 版本 与 1.14 版本对 defer 进行了两次优化,使得 defer 的性能开销在大部分场景下都得到大幅降低,其中到底经历了什么原理? 这是因为这两个版本对 defer ...
- Go语言学习——函数二 defer语句
函数 package main import "fmt" // 函数:一段代码的封装 func f1(){ fmt.Println("Hello 中国!") } ...
- go defer 语句会延迟函数的执行直到上层函数返回。
defer code... 可以理解为 执行完当前defer所在的方法代码后执行defer 中的代码 常用在释放资源 比如 关闭文件 为防止忘记编写关闭代码 可以先写好 defer 各种释放资源 ...
- 【Go入门教程3】流程(if、goto、for、switch)和函数(多个返回值、变参、传值与传指针、defer、函数作为值/类型、Panic和Recover、main函数和init函数、import)
这小节我们要介绍Go里面的流程控制以及函数操作. 流程控制 流程控制在编程语言中是最伟大的发明了,因为有了它,你可以通过很简单的流程描述来表达很复杂的逻辑.Go中流程控制分三大类:条件判断,循环控制和 ...
- go:defer
defer:延迟. 假设有调用函数A.被调用函数B,其关系如下: func A(){//调用函数 ... defer B()//被调用函数 ... return//B将延迟到return前执行 } * ...
随机推荐
- 第9月第26天 pairs和ipairs cocos2dx 动画
1. a={ ip = "127.0.0.1", port = 6789 } for i,v in pairs(a) do print(i,v) end a={1} for i,v ...
- Mysql备份文件
- linux驱动---等待队列、工作队列、Tasklets【转】
转自:https://blog.csdn.net/ezimu/article/details/54851148 概述: 等待队列.工作队列.Tasklet都是linux驱动很重要的API,下面主要从用 ...
- linux压缩日志并删除原始文件
下面的脚本可以压缩日志并删除原始文件 #!/bin/bash yesterday=`date -d '1days ago' +%Y_%m_%d` cd $ find . -name "*$y ...
- node.js 安装 测试
2014年5月1日 18:48:01 安装: 系统是centos,里边的python版本是2.4,但是node.js 源码tar包安装要求是 2.6 或者 2.7 下载python 2.7编译安装,注 ...
- linux:根据名称杀死进程
参考网址:https://www.cnblogs.com/foohack/p/5359985.html pkill -f "process_name_pattern"
- Python_oldboy_自动化运维之路_线程,进程,协程(十一)
本节内容: 线程 进程 协程 IO多路复用 自定义异步非阻塞的框架 线程和进程的介绍: 举个例子,拿甄嬛传举列线程和进程的关系: 总结:1.工作最小单元是线程,进程说白了就是提供资源的 2.一个应用程 ...
- wpf DataTemplate ColumnDefinition width equal
<Grid.ColumnDefinitions> <ColumnDefinition SharedSizeGroup="col1" /> <Colum ...
- final在类和方法中的使用
package final0; //final修饰的类不能继承//final修饰的方法不能继承public class TestFinal3 { public static void main(Str ...
- Python Extension 编译问题
Python 的一大好处是调用C扩展程序相当方便,但是 Windows 下的话,编译器版本是一个大问题.以Python27为例,官方版本时采用VC2008编译的.在setup.py中添加Extensi ...