简单聊聊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正则之命名分组方式
Apr 25 Golang
Go语言切片前或中间插入项与内置copy()函数详解
Apr 27 Golang
解决Golang中ResponseWriter的一个坑
Apr 27 Golang
golang 生成对应的数据表struct定义操作
Apr 28 Golang
解决golang结构体tag编译错误的问题
May 02 Golang
golang协程池模拟实现群发邮件功能
May 02 Golang
Go 语言结构实例分析
Jul 04 Golang
深入理解go slice结构
Sep 15 Golang
Go语言基础函数基本用法及示例详解
Nov 17 Golang
Go语言安装并操作redis的go-redis库
Apr 14 Golang
Golang Elasticsearches 批量修改查询及发送MQ
Apr 19 Golang
Golang实现可重入锁的示例代码
May 25 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伪静态页面函数附使用方法
2008/06/20 PHP
PHP中开发XML应用程序之基础篇 添加节点 删除节点 查询节点 查询节
2010/07/09 PHP
php 获取SWF动画截图示例代码
2014/02/10 PHP
php使用pear_smtp发送邮件
2016/04/15 PHP
THinkPHP获取客户端IP与IP地址查询的方法
2016/11/14 PHP
浅谈PHPANALYSIS提取关键字
2019/03/08 PHP
php实现JWT验证的实例教程
2020/11/26 PHP
jquery text,radio,checkbox,select操作实现代码
2009/07/09 Javascript
ExtJS下grid的一些属性说明
2009/12/13 Javascript
一些经常会用到的Javascript检测函数
2010/05/31 Javascript
JavaScript学习笔记之获取当前目录的实现代码
2010/12/14 Javascript
contains和compareDocumentPosition 方法来确定是否HTML节点间的关系
2011/09/13 Javascript
判断客户浏览器是否支持cookie的示例代码
2013/12/23 Javascript
javascript 用函数语句和表达式定义函数的区别详解
2014/01/06 Javascript
jQuery实现鼠标经过弹出提示信息的地图热点效果
2015/08/07 Javascript
jQuery实现带滑动条的菜单效果代码
2015/08/26 Javascript
使用Sticky组件实现带sticky效果的tab导航和滚动导航的方法
2016/03/22 Javascript
js方法数据验证的简单实例
2016/09/17 Javascript
微信小程序 出现错误:{"baseresponse":{"errcode":-80002,"errmsg":""}}解决办法
2017/02/23 Javascript
从零开始学习Node.js系列教程一:http get和post用法分析
2017/04/13 Javascript
EasyUI的DataGrid每行数据添加操作按钮的实现代码
2017/08/22 Javascript
python模拟登陆阿里妈妈生成商品推广链接
2014/04/03 Python
python中的__slots__使用示例
2015/02/26 Python
Python 爬虫之超链接 url中含有中文出错及解决办法
2017/08/03 Python
python实现多线程网页下载器
2018/04/15 Python
Python实现爬虫从网络上下载文档的实例代码
2018/06/13 Python
一篇文章搞定Python操作文件与目录
2019/08/13 Python
关于Python形参打包与解包小技巧分享
2019/08/24 Python
python实现引用其他路径包里面的模块
2020/03/09 Python
python批量生成条形码的示例
2020/10/10 Python
Django如何实现防止XSS攻击
2020/10/13 Python
美国顶级防滑鞋:Shoes For Crews
2017/03/27 全球购物
高中体育教学反思
2014/01/29 职场文书
天网工程实施方案
2014/03/26 职场文书
护校行动方案
2014/05/31 职场文书
预防职务犯罪警示教育心得体会
2016/01/15 职场文书