简单聊聊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 26 Golang
golang中切片copy复制和等号复制的区别介绍
Apr 27 Golang
Golang 实现超大文件读取的两种方法
Apr 27 Golang
go结构体嵌套的切片数组操作
Apr 28 Golang
Golang 获取文件md5校验的方法以及效率对比
May 08 Golang
go语言中http超时引发的事故解决
Jun 02 Golang
Go语言实现Snowflake雪花算法
Jun 08 Golang
Go遍历struct,map,slice的实现
Jun 13 Golang
Go语言基础知识点介绍
Jul 04 Golang
Go语言实现Base64、Base58编码与解码
Jul 26 Golang
Python测试框架pytest核心库pluggy详解
Aug 05 Golang
go goth封装第三方认证库示例详解
Aug 14 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动态图像的创建
2006/10/09 PHP
phpMyAdmin2.11.6安装配置方法
2008/08/24 PHP
php在程序中将网页生成word文档并提供下载的代码
2012/10/09 PHP
php中rename函数用法分析
2014/11/15 PHP
php计数排序算法的实现代码(附四个实例代码)
2020/03/31 PHP
几款极品的javascript压缩混淆工具
2007/05/16 Javascript
JavaScript入门教程(3) js面向对象
2009/01/31 Javascript
JavaScript 通过模式匹配实现重载
2010/08/12 Javascript
JavaScript中URL编码函数代码
2011/01/11 Javascript
jQuery拖拽div实现思路
2014/02/19 Javascript
javascript解析json数据的3种方式
2014/05/08 Javascript
Node.js开源应用框架HapiJS介绍
2015/01/14 Javascript
jquery中animate的stop()方法作用实例分析
2015/01/30 Javascript
JavaScript编写点击查看大图的页面半透明遮罩层效果实例
2016/05/09 Javascript
jQuery EasyUI API 中文帮助文档和扩展实例
2016/08/01 Javascript
js HTML5上传示例代码完整版
2016/10/10 Javascript
微信小程序+云开发实现欢迎登录注册
2019/05/24 Javascript
浅谈JavaScript中你可能不知道URL构造函数的属性
2020/07/13 Javascript
python实现textrank关键词提取
2018/06/22 Python
Python中的random.uniform()函数教程与实例解析
2019/03/02 Python
python3的数据类型及数据类型转换实例详解
2019/08/20 Python
Python 实现一个手机号码获取妹子名字的功能
2019/09/25 Python
python3通过udp实现组播数据的发送和接收操作
2020/05/05 Python
基于python实现坦克大战游戏
2020/10/27 Python
css3和jquery实现自定义checkbox和radiobox组件
2014/04/22 HTML / CSS
利用css3画个同心圆示例代码
2017/07/03 HTML / CSS
Madewell美德威尔美国官网:美国休闲服饰品牌
2016/11/25 全球购物
什么是smarty? Smarty的优点是什么?
2013/08/11 面试题
社区交通安全实施方案
2014/03/22 职场文书
优秀教导主任事迹材料
2014/05/09 职场文书
团结就是力量演讲稿
2014/05/21 职场文书
安全生产标语
2014/06/06 职场文书
企业标语口号
2014/06/10 职场文书
优秀三好学生事迹材料
2014/08/31 职场文书
党支部党的群众路线对照检查材料
2014/09/24 职场文书
2015年上半年物业工作总结
2015/03/30 职场文书