简单聊聊Golang中defer预计算参数

 更新时间:2022年03月25日 16:53:18   作者:蓝色记忆  
在golang当中defer代码块会在函数调用链表中增加一个函数调用,下面这篇文章主要给大家介绍了关于Golang中defer预计算参数的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

什么是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通过node_exporter监控GPU及cpu频率、温度的代码

    golang通过node_exporter监控GPU及cpu频率、温度的代码

    node_exporter这个开源组件是配合prometheus收集主机操作系统层的metrics的常用组件,但是官方没有提供GPU卡的metrics的采集,今天通过本文给大家介绍golang通过node_exporter监控GPU及cpu频率、温度的相关知识,感兴趣的朋友一起看看吧
    2022-05-05
  • go mock模拟接口的实现

    go mock模拟接口的实现

    本文主要介绍了go mock模拟接口的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • GoLang完整实现快速列表

    GoLang完整实现快速列表

    这篇文章主要介绍了GoLang完整实现快速列表,列表是一种非连续的存储容器,由多个节点组成,节点通过一些 变量 记录彼此之间的关系,列表有多种实现方法,如单链表、双链表等
    2022-12-12
  • Golang中Bit数组的实现方式

    Golang中Bit数组的实现方式

    这篇文章主要介绍了Golang中Bit数组的实现方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-04-04
  • Golang初始化MySQL数据库方法浅析

    Golang初始化MySQL数据库方法浅析

    这篇文章主要介绍了Golang初始化MySQL数据库的方法,数据库的建立第一步即要初始化,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
    2023-05-05
  • Go语言fmt.Sprintf格式化输出的语法与实例

    Go语言fmt.Sprintf格式化输出的语法与实例

    Go 可以使用 fmt.Sprintf 来格式化字符串,下面这篇文章主要给大家介绍了关于Go语言fmt.Sprintf格式化输出的语法与实例,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • Go工具链之go tool fix用法详解

    Go工具链之go tool fix用法详解

    go tool fix 是 Go 工具链中的一个命令,作用是把指定 Go 程序代码包中的的所有旧版本代码修正为新版本的代码,本文将简单介绍一下go tool fix的使用方法,感兴趣的小伙伴可以参考阅读下
    2023-07-07
  • Go并发控制Channel使用场景分析

    Go并发控制Channel使用场景分析

    使用channel来控制子协程的优点是实现简单,缺点是当需要大量创建协程时就需要有相同数量的channel,而且对于子协程继续派生出来的协程不方便控制
    2021-07-07
  • 详解go-zero是如何做路由管理的

    详解go-zero是如何做路由管理的

    go-zero 是一个微服务框架,包含了 web 和 rpc 两大部分,而对于 web 框架来说,路由管理是必不可少的一部分,那么本文就来探讨一下 go-zero 的路由管理是怎么做的吧
    2023-08-08
  • Go语言实现配置热加载的方法分享

    Go语言实现配置热加载的方法分享

    web项目,经常需要热启动各种各样的配置信息,一旦这些服务发生变更,我们需要重新启动web server,以使配置生效,实现配置热加载,本文为大家整理了几个方法实现这个需求,需要的可以参考下
    2023-05-05

最新评论