简单聊聊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 Gin实现文件上传下载的示例代码
Apr 02 Golang
Go缓冲channel和非缓冲channel的区别说明
Apr 25 Golang
golang 如何用反射reflect操作结构体
Apr 28 Golang
解决goland 导入项目后import里的包报红问题
May 06 Golang
Golang实现AES对称加密的过程详解
May 20 Golang
go语言中http超时引发的事故解决
Jun 02 Golang
浅谈Golang 切片(slice)扩容机制的原理
Jun 09 Golang
Go语言空白表示符_的实例用法
Jul 04 Golang
golang fmt格式“占位符”的实例用法详解
Jul 04 Golang
Go语言基础函数基本用法及示例详解
Nov 17 Golang
Golang 链表的学习和使用
Apr 19 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微信开发之二维码生成类
2015/06/26 PHP
Aster vs KG BO3 第一场2.19
2021/03/10 DOTA
Jquery实现点击切换图片并隐藏显示内容(2种方法实现)
2013/04/11 Javascript
使用JQUERY进行后台页面布局控制DIV实现左右式
2014/01/07 Javascript
让JavaScript的Alert弹出框失效的方法禁止弹出警告框
2014/09/03 Javascript
jquery.idTabs 选项卡使用示例代码
2014/09/03 Javascript
jQuery中removeClass()方法用法实例
2015/01/05 Javascript
jQuery实现页面评论栏中访客信息自动填写功能的方法
2016/05/23 Javascript
JS实现点击表头表格自动排序(含数字、字符串、日期)
2017/01/22 Javascript
javascript 判断当前浏览器版本并判断ie版本
2017/02/17 Javascript
jQuery实现一个简单的轮播图
2017/02/19 Javascript
vue.js开发环境安装教程
2017/03/17 Javascript
nodejs学习笔记之路由
2017/03/27 NodeJs
简化vuex的状态管理方案的方法
2018/06/02 Javascript
layui异步加载table表中某一列数据的例子
2019/09/16 Javascript
js实现GIF动图分解成多帧图片上传
2019/10/24 Javascript
基于jQuery实现可编辑的表格
2019/12/11 jQuery
python使用PyV8执行javascript代码示例分享
2013/12/04 Python
Python 字符串操作(string替换、删除、截取、复制、连接、比较、查找、包含、大小写转换、分割等)
2018/03/19 Python
Django框架的使用教程路由请求响应的方法
2018/07/03 Python
基于DataFrame改变列类型的方法
2018/07/25 Python
Django如何实现网站注册用户邮箱验证功能
2019/08/14 Python
浅谈Pycharm最有必要改的几个默认设置项
2020/02/14 Python
浅谈Python中threading join和setDaemon用法及区别说明
2020/05/02 Python
h5使用canvas画布实现手势解锁
2019/01/04 HTML / CSS
美国领先的医疗警报服务:Philips Lifeline
2018/03/12 全球购物
大学生党员自我批评
2014/02/14 职场文书
2014党员四风对照检查材料思想汇报
2014/09/17 职场文书
党员批评与自我批评(5篇)
2014/09/23 职场文书
2014个人年度工作总结范文
2014/12/24 职场文书
责任书范本大全
2015/05/11 职场文书
2015军训通讯稿大全
2015/07/18 职场文书
先进教师个人主要事迹材料
2015/11/03 职场文书
JavaScript利用html5新方法操作元素类名详解
2021/11/27 Javascript
tomcat的catalina.out日志按自定义时间格式进行分割的操作方法
2022/04/02 Servers