简单聊聊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语言map与string的相互转换的实现
Apr 07 Golang
Go使用协程交替打印字符
Apr 29 Golang
golang DNS服务器的简单实现操作
Apr 30 Golang
解决golang 关于全局变量的坑
May 06 Golang
Go遍历struct,map,slice的实现
Jun 13 Golang
K8s部署发布Golang应用程序的实现方法
Jul 16 Golang
深入理解go slice结构
Sep 15 Golang
Go语言基础map用法及示例详解
Nov 17 Golang
golang语言指针操作
Apr 14 Golang
golang定时器
Apr 14 Golang
GO语言异常处理分析 err接口及defer延迟
Apr 14 Golang
Golang jwt身份认证
Apr 20 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运算符的知识大全
2011/11/03 PHP
简介WordPress中用于获取首页和站点链接的PHP函数
2015/12/17 PHP
Zend Framework教程之Zend_Config_Ini用法分析
2016/03/23 PHP
php中yar框架实例用法讲解
2020/12/27 PHP
THREE.JS入门教程(2)着色器-上
2013/01/24 Javascript
js操作checkbox遇到的问题解决
2013/06/29 Javascript
jQuery不使用插件及swf实现无刷新文件上传
2014/12/08 Javascript
基于JavaScript实现移除(删除)数组中指定元素
2016/01/04 Javascript
实现高性能JavaScript之执行与加载
2016/01/30 Javascript
浅析JavaScript中的对象类型Object
2016/05/26 Javascript
Google 地图控件集详解及实例代码
2016/08/06 Javascript
微信小程序注册60s倒计时功能 使用JS实现注册60s倒计时功能
2017/08/16 Javascript
详解vue-cli 接口代理配置
2017/12/13 Javascript
vue中的provide/inject的学习使用
2018/05/09 Javascript
Makefile/cmake/node-gyp中区分判断不同平台的方法
2018/12/18 Javascript
python使用smtplib模块通过gmail实现邮件发送的方法
2015/05/08 Python
python DataFrame获取行数、列数、索引及第几行第几列的值方法
2018/04/08 Python
Python 实现域名解析为ip的方法
2019/02/14 Python
Python+OpenCV实现实时眼动追踪的示例代码
2019/11/11 Python
tensorflow通过模型文件,使用tensorboard查看其模型图Graph方式
2020/01/23 Python
python之语音识别speech模块
2020/09/09 Python
Python 使用SFTP和FTP实现对服务器的文件下载功能
2020/12/17 Python
基于Html5 canvas实现裁剪图片和马赛克功能及又拍云上传图片 功能
2019/07/09 HTML / CSS
Manduka官网:瑜伽垫、瑜伽毛巾和服装
2018/07/02 全球购物
Stokke美国官方网店:高级儿童家具、推车、汽车座椅和配件
2020/06/06 全球购物
JPA的特点
2014/10/25 面试题
保健品市场营销方案
2014/03/31 职场文书
购房协议书范本
2014/04/11 职场文书
爱心捐助倡议书
2014/05/19 职场文书
责任书范本
2014/08/25 职场文书
公司股份合作协议书
2014/12/07 职场文书
廉洁自律承诺书范文
2015/04/28 职场文书
MySQL 数据类型选择原则
2021/05/27 MySQL
postgresql 删除重复数据案例详解
2021/08/02 PostgreSQL
在项目中使用redis做缓存的一些思路
2021/09/14 Redis
用 Python 定义 Schema 并生成 Parquet 文件详情
2021/09/25 Python