简单聊聊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中semaphore(信号量)源码
Apr 03 Golang
Go语言中break label与goto label的区别
Apr 28 Golang
Go 自定义package包设置与导入操作
May 06 Golang
Go语言实现Snowflake雪花算法
Jun 08 Golang
Go语言设计模式之结构型模式
Jun 22 Golang
go语言使用Casbin实现角色的权限控制
Jun 26 Golang
golang 实用库gotable的具体使用
Jul 01 Golang
Go 语言中 20 个占位符的整理
Oct 16 Golang
golang定时器
Apr 14 Golang
GO语言异常处理分析 err接口及defer延迟
Apr 14 Golang
Golang 入门 之url 包
May 04 Golang
Go语言入门exec的基本使用
May 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调用三种数据库的方法(1)
2006/10/09 PHP
php数组中删除元素的实现代码
2012/06/22 PHP
php、java、android、ios通用的3des方法(推荐)
2016/09/09 PHP
Laravel框架中Blade模板的用法示例
2017/08/30 PHP
php实现的rc4加密解密类定义与用法示例
2018/08/16 PHP
Extjs学习笔记之九 数据模型(上)
2010/01/11 Javascript
JSON 学习之JSON in JavaScript详细使用说明
2010/02/23 Javascript
nodejs 后缀名判断限制代码
2011/03/31 NodeJs
display和visibility的区别示例介绍
2014/02/26 Javascript
javascript学习笔记(一)基础知识
2014/09/30 Javascript
JS实现控制表格只显示行边框或者只显示列边框的方法
2015/03/31 Javascript
轻松学习jQuery插件EasyUI EasyUI实现拖放商品放置购物车
2015/11/30 Javascript
jQuery返回定位插件详解
2017/05/15 jQuery
Node.js 回调函数实例详解
2017/07/06 Javascript
详细介绍Ruby中的正则表达式
2015/04/10 Python
使用python获取csv文本的某行或某列数据的实例
2018/04/03 Python
python实现飞机大战游戏
2020/10/26 Python
python实现一行输入多个值和一行输出多个值的例子
2019/07/16 Python
详解Python绘图Turtle库
2019/10/12 Python
python实现简单学生信息管理系统
2020/04/09 Python
使用darknet框架的imagenet数据分类预训练操作
2020/07/07 Python
使用numpngw和matplotlib生成png动画的示例代码
2021/01/24 Python
python实现学生信息管理系统源码
2021/02/22 Python
详解CSS3中字体平滑处理和抗锯齿渲染
2017/03/29 HTML / CSS
HTML5印章绘制电子签章图片(中文英文椭圆章、中文英文椭圆印章)
2019/06/03 HTML / CSS
俄罗斯电子产品、计算机和家用电器购物网站:OLDI
2019/10/27 全球购物
Linux的主要特性
2016/09/03 面试题
食品销售计划书
2014/04/26 职场文书
师德师风演讲稿
2014/05/05 职场文书
农业开发项目建议书
2014/05/16 职场文书
学习之星事迹材料
2014/05/17 职场文书
俞敏洪一分钟演讲稿
2014/08/26 职场文书
党的群众路线查摆剖析材料
2014/10/10 职场文书
班级光棍节联谊会策划书
2014/10/10 职场文书
晚会开幕词
2015/01/28 职场文书
敬业奉献模范事迹材料(2016精选版)
2016/02/26 职场文书