简单聊聊Golang中defer预计算参数


Posted in Golang onMarch 25, 2022

什么是defer

defer用来声明一个延迟函数,把这个函数放入到一个栈上, 当外部的包含方法return之前,返回参数到调用方法之前调用,也可以说是运行到最外层方法体的"}"时调用。我们经常用他来做一些资源的释放,比如关闭io操作

func doSomething(fileName string) {
    file,err := os.Open(fileName)
    if err != nil {
    panic(err)
    }
    defer file.Close()
}

defer 可以保证方法可以在外围函数返回之前调用。有点像其他言的 try finally

try{
}finally{
}

Go语言defer预计算参数

Go 语言中所有的函数调用都是传值的,虽然 defer 是关键字,但是也继承了这个特性。假设我们想要计算 main 函数运行的时间,可能会写出以下的代码:

package main
import (
	"fmt"
	"time"
)

func main() {
	startedAt := time.Now()
	defer fmt.Println(time.Since(startedAt))
	time.Sleep(time.Second) //休眠一秒
}

结果是:

D:\workspace\go\src\test>go run main.go
0s 

运行结果并不符合我们的预期,这个现象背后的原因是什么呢?经过分析,我们会发现调用 defer 关键字会立刻拷贝函数中引用的外部参数,所以 time.Since(startedAt) 的结果不是在 main 函数退出之前计算的,而是在 defer 关键字调用时计算的【defer入栈的时候】,最终导致上述代码输出 0s

我们再来看个简单例子来说明上述解释:

package main
import (
	"fmt"
)

func main() {
	i := 1
	defer fmt.Println(test(i))
	i = 100
}

func test(i int) int {
	i = i + 1
	return i
} 

D:\workspace\go\src\test>go run main.go
2

当代码运行到defer fmt.Println(test(i))的时候,会把defer右边最外层函数的参数计算完毕,并传递进函数里,但不会执行函数体的代码直到包裹defer的函数返回。我们先看会把defer右边最外层函数的参数计算完毕,并传递进函数里这句话,对应例子就是先把test(i)算出来,此时i=1,计算test(1)得2,然后fmt.Println(2)入栈,等到最后程序运行完了再运行defer结果就是2(但不会执行函数体的代码直到包裹defer的函数返回)。

我们再来看一个例子与匿名函数结合:

package main
import (
	"fmt"
)

func main() {
	i := 1
	defer func() {
		fmt.Println(test(i))
	}()
	i = 100
}

func test(i int) int {
	i = i + 1
	return i
}

结果:

D:\workspace\go\src\test>go run main.go
101  

使用匿名函数,结果是101,相当于i给到test方法的是100,那为什么呢?还是那句话:但不会执行函数体的代码直到包裹defer的函数返回

也就是说他会把整个{ fmt.Println(test(i)) }()函数体入栈,等到最后程序运行完了再运行defer,此时的i是100,运行test后就是101了。

所以你要解决第一个打印为0s的问题,你就可以使用匿名函数来解决,如下:

package main
import (
	"fmt"
	"time"
)

func main() {
	startedAt := time.Now()
	defer func() {
		fmt.Println(time.Since(startedAt))
	}()
	time.Sleep(time.Second) //休眠一秒
}

结果:

D:\workspace\go\src\test>go run main.go
1.0152825s

总结

到此这篇关于Golang中defer预计算参数的文章就介绍到这了,更多相关Go defer预计算参数内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Golang 相关文章推荐
解决Golang中ResponseWriter的一个坑
Apr 27 Golang
golang 在windows中设置环境变量的操作
Apr 29 Golang
golang 比较浮点数的大小方式
May 02 Golang
Go标准容器之Ring的使用说明
May 05 Golang
浅谈Golang 切片(slice)扩容机制的原理
Jun 09 Golang
Golang表示枚举类型的详细讲解
Sep 04 Golang
Go语言特点及基本数据类型使用详解
Mar 21 Golang
Go语言安装并操作redis的go-redis库
Apr 14 Golang
Golang 对es的操作实例
Apr 20 Golang
详解Go语言中配置文件使用与日志配置
Jun 01 Golang
Golang gRPC HTTP协议转换示例
Jun 16 Golang
Go微服务项目配置文件的定义和读取示例详解
Jun 21 Golang
Go 中的空白标识符下划线
golang生成vcf通讯录格式文件详情
golang实现浏览器导出excel文件功能
Golang使用Panic与Recover进行错误捕获
Mar 22 #Golang
Go语言特点及基本数据类型使用详解
详解Golang如何优雅的终止一个服务
Mar 21 #Golang
Go语言实现一个简单的并发聊天室的项目实战
Mar 18 #Golang
You might like
php htmlspecialchars加强版
2010/02/16 PHP
Laravel中日期时间处理包Carbon的简单使用
2017/09/21 PHP
基于Jquery的开发个代阴影的对话框效果代码
2011/07/28 Javascript
js创建数据共享接口——简化框架之间相互传值
2011/10/23 Javascript
浅析JQuery获取和设置Select选项的常用方法总结
2013/07/04 Javascript
Jquery多选框互相内容交换的实例代码
2013/07/04 Javascript
javascript 手动给表增加数据的小例子
2013/07/10 Javascript
JavaScript实现打字效果的方法
2015/07/10 Javascript
js图片轮播手动切换效果
2015/11/10 Javascript
JavaScript优化专题之Loading and Execution加载和运行
2016/01/20 Javascript
JS生成不重复的随机数组的简单实例
2016/07/10 Javascript
全面了解构造函数继承关键apply call
2016/07/26 Javascript
weUI应用之JS常用信息提示弹层的封装
2016/11/21 Javascript
JavaScript面试题(指针、帽子和女朋友)
2016/11/23 Javascript
jQuery选择器特殊字符与属性空格问题
2017/08/14 jQuery
详解原生JS动态添加和删除类
2019/03/26 Javascript
vue使用lodop打印控件实现浏览器兼容打印的方法
2021/02/07 Vue.js
Python面向对象编程中的类和对象学习教程
2015/03/30 Python
在IPython中进行Python程序执行时间的测量方法
2018/11/01 Python
Python中的异常处理try/except/finally/raise用法分析
2019/02/28 Python
Python 中判断列表是否为空的方法
2019/11/24 Python
对python中各个response的使用说明
2020/03/28 Python
python使用自定义钉钉机器人的示例代码
2020/06/24 Python
如何使用Python处理HDF格式数据及可视化问题
2020/06/24 Python
Python matplotlib图例放在外侧保存时显示不完整问题解决
2020/07/28 Python
python中time包实例详解
2021/02/02 Python
全球烹饪课程的领先预订平台:Cookly
2020/01/28 全球购物
EJB包括(SessionBean,EntityBean)说出他们的生命周期,及如何管理事务的?
2013/02/17 面试题
市场营销职业生涯规划书范文
2014/01/12 职场文书
社区八一活动方案
2014/02/03 职场文书
机械制造专业毕业生求职信
2014/03/02 职场文书
关于环保的演讲稿
2014/05/10 职场文书
Python图像处理之图像拼接
2021/04/28 Python
Jedis操作Redis实现模拟验证码发送功能
2021/09/25 Redis
关于windows server 2012 DC 环境 重启后蓝屏代码:0xc00002e2的问题
2022/05/25 Servers
Springboot中如何自动转JSON输出
2022/06/16 Java/Android