简单聊聊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 相关文章推荐
Go语言操作数据库及其常规操作的示例代码
Apr 21 Golang
解决Golang中ResponseWriter的一个坑
Apr 27 Golang
go语言中json数据的读取和写出操作
Apr 28 Golang
golang 实现对Map进行键值自定义排序
Apr 28 Golang
彻底理解golang中什么是nil
Apr 29 Golang
Golang: 内建容器的用法
May 05 Golang
Golang 编译成DLL文件的操作
May 06 Golang
详解Go语言Slice作为函数参数的使用
Jul 02 Golang
go开发alertmanger实现钉钉报警
Jul 16 Golang
手把手教你导入Go语言第三方库
Aug 04 Golang
GO中sync包自由控制并发示例详解
Aug 05 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类中的魔术方法(Magic Method)简明总结
2014/07/08 PHP
php脚本运行时的超时机制详解
2016/02/17 PHP
28个JS验证函数收集
2010/03/02 Javascript
JS/Jquery判断对象为空的方法
2015/06/11 Javascript
JavaScript jQuery 中定义数组与操作及jquery数组操作
2015/12/18 Javascript
BootStrap使用file-input插件上传图片的方法
2016/09/05 Javascript
js获取一组日期中最近连续的天数
2017/05/25 Javascript
使用Node.js实现RESTful API的示例
2017/08/01 Javascript
详解vue-cli构建项目反向代理配置
2017/09/07 Javascript
详解项目升级到vue-cli3的正确姿势
2019/01/28 Javascript
Layer+Echarts构建弹出层折线图的方法
2019/09/25 Javascript
解决Vue 移动端点击出现300毫秒延迟的问题
2020/07/21 Javascript
vue-calendar-component 封装多日期选择组件的实例代码
2020/12/04 Vue.js
[01:02:00]DOTA2-DPC中国联赛 正赛 Elephant vs IG BO3 第三场 1月24日
2021/03/11 DOTA
python调用新浪微博API项目实践
2014/07/28 Python
解决已经安装requests,却依然提示No module named requests问题
2018/05/18 Python
python频繁写入文件时提速的方法
2019/06/26 Python
python 计算方位角实例(根据两点的坐标计算)
2020/01/17 Python
Python 实现黑客帝国中的字符雨的示例代码
2020/02/20 Python
200行python代码实现贪吃蛇游戏
2020/04/24 Python
CSS3 完美实现圆角效果
2009/07/13 HTML / CSS
Mavi牛仔裤美国官网:土耳其著名牛仔品牌
2016/09/24 全球购物
英国露营设备和户外服装购物网站:Simply Hike
2019/05/05 全球购物
大专计算机个人求职的自我评价
2013/10/21 职场文书
大四学生毕业自荐信
2013/11/07 职场文书
技术总监的工作职责
2013/11/13 职场文书
个人查摆剖析材料
2014/02/04 职场文书
我们的节日清明节活动方案
2014/03/05 职场文书
共产党员承诺书
2014/03/25 职场文书
遗嘱公证书标准样本
2014/04/08 职场文书
金融系毕业生自荐书
2014/07/08 职场文书
对外汉语教师推荐信
2015/03/27 职场文书
中秋联欢会主持词
2015/07/04 职场文书
手术室消毒隔离制度
2015/08/05 职场文书
2015年大学组织委员个人工作总结
2015/10/23 职场文书
python基于tkinter制作无损音乐下载工具
2021/03/29 Python