简单聊聊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如何替换某个文件中的字符串
Apr 25 Golang
go语言中切片与内存复制 memcpy 的实现操作
Apr 27 Golang
go 实现简易端口扫描的示例
May 22 Golang
Go timer如何调度
Jun 09 Golang
go语言使用Casbin实现角色的权限控制
Jun 26 Golang
Go语言应该什么情况使用指针
Jul 25 Golang
Golang 并发下的问题定位及解决方案
Mar 16 Golang
Go语言安装并操作redis的go-redis库
Apr 14 Golang
Golang 字符串的常见操作
Apr 19 Golang
Golang 对es的操作实例
Apr 20 Golang
Golang 切片(Slice)实现增删改查
Apr 22 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脚本中include文件出错解决方法
2008/11/20 PHP
深入掌握include_once与require_once的区别
2013/06/17 PHP
利用phpexcel把excel导入数据库和数据库导出excel实现
2014/01/09 PHP
php生成随机数的三种方法
2014/09/10 PHP
php中magic_quotes_gpc对unserialize的影响分析
2014/12/16 PHP
CentOS 上搭建 PHP7 开发测试环境
2017/02/26 PHP
PHP保存Base64图片base64_decode的问题整理
2019/11/04 PHP
用函数模板,写一个简单高效的 JSON 查询器的方法介绍
2013/04/17 Javascript
JS基于myFocus库实现各种功能的tab选项卡切换效果
2015/09/19 Javascript
Three.js学习之Lamber材质和Phong材质
2016/08/04 Javascript
JS实现的适合做faq或menu滑动效果示例
2016/11/17 Javascript
使用requirejs模块化开发多页面一个入口js的使用方式
2017/06/14 Javascript
webpack4.0打包优化策略整理小结
2018/03/30 Javascript
详解vue-cli官方脚手架配置
2018/07/20 Javascript
vue.js 图片上传并预览及图片更换功能的实现代码
2018/08/27 Javascript
详解BootStrap表单验证中重置BootStrap-select验证提示不清除的坑
2019/09/17 Javascript
js实现div色块碰撞
2020/01/16 Javascript
解决vuecli3中img src 的引入问题
2020/08/04 Javascript
如何搭建一个完整的Vue3.0+ts的项目步骤
2020/10/18 Javascript
[06:38]DOTA2怒掀电竞风暴 2013Chinajoy
2013/07/27 DOTA
Python网络爬虫中的同步与异步示例详解
2018/02/03 Python
python3的输入方式及多组输入方法
2018/10/17 Python
wxPython实现文本框基础组件
2019/11/18 Python
html5+css3气泡组件的实现
2014/11/21 HTML / CSS
canvas 实现 github404动态效果的示例代码
2017/11/15 HTML / CSS
英国Boots旗下太阳镜网站:Boots Designer Sunglasses
2018/07/07 全球购物
机电工程专业应届生求职信
2013/10/03 职场文书
采购主管的岗位职责
2013/12/17 职场文书
煤矿班组长岗位职责
2013/12/29 职场文书
个人党性分析总结
2015/03/05 职场文书
《钓鱼的启示》教学反思
2016/02/18 职场文书
优秀新员工事迹材料
2019/05/13 职场文书
CocosCreator如何实现划过的位置显示纹理
2021/04/14 Javascript
vue-cropper插件实现图片截取上传组件封装
2021/05/27 Vue.js
React forwardRef的使用方法及注意点
2021/06/13 Javascript
Kubernetes控制节点的部署
2022/04/01 Servers