- 前言
- Go 与操作系统
- Go 内部机制
- Go 基本数据类型
- 4 组合类型的使用
- 5 数据结构
- 6 Go package 中不为人知的知识
- 7 反射和接口
- 8 Go UNIX 系统编程
- 08.1 关于 UNIX 进程
- 08.2 flag 包
- 8.2 flag 包
- 08.3 io.Reader 和 io.Writer 接口
- 08.4 bufio 包
- 08.5 读取文本文件
- 08.6 从文件中读取所需的数据量
- 08.7 为什么我们使用二进制格式
- 08.8 读取 CSV 文件
- 08.9 写入文件
- 08.10 从磁盘加载和保存数据
- 08.11 再看strings包
- 08.12 关于bytes包
- 08.13 文件权限
- 08.14 处理 Unix 信号
- 08.15 Unix 管道编程
- 08.16 遍历目录树
- 08.17 使用 ePBF
- 08.18 关于 syscall.PtraceRegs
- 08.19 跟踪系统调用
- 08.20 User ID 和 group ID
- 08.21 其他资源
- 08.22 练习
- 08.23 总结
- 9 并发 Goroutines、Channel 和 Pipeline
- 10 Go 并发-进阶讨论
- 11 代码测试、优化及分析
- 12 Go 网络编程基础
- 13 网络编程 - 构建服务器与客户端
02.6 defer 关键字
defer关键字
defer关键字的作用是当外围函数返回之后才执行被推迟的函数。在文件输入输出操作中经常可以见到defer关键字,因为它使您不必记住何时关闭已打开的文件:defer关键字调用文件关闭函数关闭已打开的文件时,可以紧靠着文件打开函数之后。在第八章“告诉Unix系统要做什么”中,会介绍如何在文件相关操作中使用defer关键字,本节将介绍defer的其他用法。您还将在 panic()
和 recover()
两个Go内置函数中看到defer操作。
首先记住一点重要的原则:defer函数在外围函数返回之后,以后进先出(LIFO)的原则执行。简单点说,在一个外围函数中有3个defer函数: f1()
最先出现,然后 f2()
,最后 f3()
,当外围函数执行返回之后, f3()
最先被执行,接着是 f2()
,最后是 f1()
。
如果你感觉这个定义不是很清晰,查看并执行 defer.go
源码,这应该会让你理解的更清楚些。下面分三部分呈现这段源码。
源码第一部分:
package main import ( "fmt" ) func d1() { for i := 3; i > 0; i-- { defer fmt.Print(i, " ") } }
上面的Go代码实现了一个名为 d1()
函数,函数里面有一个 for
循环和一个 defer
语句,这个 defer
语句将会执行三次。
第二部分源码:
> func d2() {
> for i := 3; i > 0; i-- {
> defer func() {
> fmt.Print(i, " ")
> }()
> }
> fmt.Println()
> }
这部分代码实现了 d2()
函数,它也包含了一个 for
循环和一个 defer
语句,这个 defer
语句也会执行三次,但是这个 defer
执行的是匿名函数,并且匿名函数没有带参数。
最后一部分源码:
> func d3() {
> for i := 3; i > 0; i-- {
> defer func(n int) {
> fmt.Print(n, " ")
> }(i)
> }
> }
> func main() {
> d1()
> fmt.Println()
> d2()
> fmt.Println()
> d3()
> fmt.Println()
> }
这部分代码,定义了 d3()
函数,它包含了 for
循环 defer
语句,这个 defer
执行带参数的匿名函数,其中参数n使用的是循环中变量i的值。 main()
函数调用这三个函数。
执行源码会得到如下输出:
$ go run defer.go 1 2 3 0 0 0 1 2 3
您很可能会发现生成的输出很复杂且难以理解。 这表明,如果您的代码不清晰或不明确,执行 defer
操作可能会产生非常棘手的结果。
让我们分析一下上面的结果,更进一步了解如果不密切关注自己的代码, defer
将会变得多么棘手。第一行是 d1()
函数输出的(1 2 3),变量i的值在这个函数中顺序是3 、2和1 ,在 d1()
中延迟的函数是 fmt.Print()
。 因此,当 d1()
函数即将返回时,您将以相反的顺序获取for循环中变量i的三个值,因为被延迟的函数以 LIFO
顺序执行。
第二行是 d2()
函数的输出,很奇怪的是我们得到了单个0,而不是想象中的1、2和3 ,原因很简单,在循环结束后,i的值为0,因为是0值使循环终止。但是,这里棘手的部分是在循环结束后会执行被延迟的匿名函数。因为它没有参数,这意味着,将值为0的i进行三次打印输出! 在您的项目中,这种令人困惑的代码可能导致令人讨厌的错误,所以尽量避免它!
第三行是 d3()
函数的输出,因为匿名函数的参数的存在,每次匿名函数被推迟执行的时候,它都会获取并使用变量i当前的值,所以每次执行匿名函数都有不同的值输出。
到这里,你应该清楚了,使用 defer
的最好的方式就是第三个函数展示的方法,这是因为您故意以易于理解的方式在匿名函数中传递所需的变量。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论