[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前执行 } * ...
随机推荐
- Nessus扫描策略
本篇将简单介绍下Nessus的扫描策略设置.选用plugins及如何使用定制的策略来进行扫描任务. Step 1: 启动Nessus服务 root@kali:~# /etc/init.d/nessus ...
- AC自动机(Keywords Search)
题目链接:https://cn.vjudge.net/contest/280743#problem/A 题目大意:首先给你T组测试样例,然后给你n个字符串,最后再给你一个模式串,然后问你这一些字符串中 ...
- orm 缺点
背景 提起orm,在我开发这几年可是阴魂不散,因为我的开发没人带,全是自己琢磨,好处是很多东西都懂,都理解的透彻,缺点是见得少,接触少.而我一直没用orm,但是又到处听说orm,但我总想不明白有啥用处 ...
- appium无ID、name定位处理【转】
1.关于没有name,没有ID的元素的定位---通用篇解题思路:因为没有name,id:其实剩下的选择已不多,要么xpath,要么className.xpath木有好印象(稳定性不高,加之1.0x后需 ...
- 修改MySQL的时区,涉及参数time_zone
原地址:http://blog.csdn.net/mchdba/article/details/9763521 首先需要查看mysql的当前时区,用time_zone参数 mysql> show ...
- 转:vue2.0 keep-alive最佳实践
转载至:https://segmentfault.com/a/1190000008123035 1.基本用法 vue2.0提供了一个keep-alive组件用来缓存组件,避免多次加载相应的组件,减少性 ...
- private,protected,public和default的区别
private,protected,public和default的区别 private,protected,public和default作为Java中的访问修饰符,他们的最大区别就在于访问权限不同: ...
- java 接口与工厂
接口时实现多重继承的途径,而生产遵循某个接口的对象的典型方式就是工厂方法设计模式,这与直接调用构造器不同,我们在工厂对象上调用的是某种方法,而该工厂对象将生成接口的某个实现的对象,理论上通过这种方式, ...
- 欢迎来到abc2237512422的博客
这是第一篇博文. 本博客已迁移到 abc233.site
- 墨刀 vs Axure RP
https://www.jianshu.com/p/b4b9c1f15304 墨刀https://modao.cc/ Axure RP https://www.axure.com/https://ww ...