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

目录
  • 什么是defer
  • Go语言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预计算参数内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Golang巧用defer进行错误处理的方法

    本文主要跟大家介绍了Golang巧用defer进行错误处理的相关内容,分享出来供大家参考学习,下面来看看详细的介绍: 问题引入 毫无疑问,错误处理是程序的重要组成部分,有效且优雅的处理错误是大多数程序员的追求.很多程序员都有C/C++的编程背景,Golang的程序员也不例外,他们处理错误有意无意的带着C/C++的烙印. 我们看看下面的例子,就有一种似曾相识的赶脚,代码如下: func deferDemo() error { err := createResource1() if err != n

  • golang中defer的使用规则详解

    前言 在golang当中,defer代码块会在函数调用链表中增加一个函数调用.这个函数调用不是普通的函数调用,而是会在函数正常返回,也就是return之后添加一个函数调用.因此,defer通常用来释放函数内部变量. 为了更好的学习defer的行为,我们首先来看下面一段代码: func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil {

  • GO语言Defer用法实例分析

    本文实例讲述了GO语言Defer用法.分享给大家供大家参考.具体分析如下: defer:调用一个被 defer 的函数时在函数刚要返回之前延迟执行,当函数无论怎样返回,某资源必须释放时,可用这种与众不同.但有效的处理方式.传统的例子包括解锁互斥或关闭文件. 这样延迟一个函数有双重优势:一是你永远不会忘记关闭文件,此错误在你事后编辑函数添加一个返回路径时常常发生.二是关闭和打开靠在一起,比放在函数尾要清晰很多. 复制代码 代码如下: /**  * Created with IntelliJ IDE

  • 总结Go语言中defer的使用和注意要点

    前言 defer是golang语言中的关键字,用于资源的释放,会在函数返回之前进行调用. 一般采用如下模式: f,err := os.Open(filename) if err != nil { panic(err) } defer f.Close() 如果有多个defer表达式,调用顺序类似于栈,越后面的defer表达式越先被调用. 延时调用函数的语法如下: defer func_name(param-list) 当一个函数调用前有关键字 defer 时, 那么这个函数的执行会推迟到包含这个

  • 聊聊golang中多个defer的执行顺序

    golang 中多个 defer 的执行顺序 引用 Ture Go 中的一个示例: package main import "fmt" func main() { fmt.Println("counting") for i := 0; i < 10; i++ { defer fmt.Println(i) } fmt.Println("done") } 程序执行结果为: counting done 9 8 7 6 5 4 3 2 1 0 从结

  • Go使用defer函数要注意的几个点

    概述 defer 函数大家肯定都用过,它在声明时不会立刻去执行,而是在函数 return 后去执行的. 它的主要应用场景有异常处理.记录日志.清理数据.释放资源 等等. 这篇文章不是分享 defer 的应用场景,而是分享使用 defer 需要注意的点. 咱们先从一道题开始,一起来感受下 ... func calc(index string, a, b int) int { ret := a + b fmt.Println(index, a, b, ret) return ret } func m

  • golang中defer的关键特性示例详解

    前言 大家都知道golang的defer关键字,它可以在函数返回前执行一些操作,最常用的就是打开一个资源(例如一个文件.数据库连接等)时就用defer延迟关闭改资源,以免引起内存泄漏.本文主要给大家介绍了关于golang中defer的关键特性,分享出来供大家参考学习,下面话不多说,来一起看看详细的介绍: 一.defer 的作用和执行时机 go 的 defer 语句是用来延迟执行函数的,而且延迟发生在调用函数 return 之后,比如 func a() int { defer b() return

  • Go语言中的延迟函数defer示例详解

    前言 大家都知道go语言的defer功能很强大,对于资源管理非常方便,但是如果没用好,也会有陷阱哦.Go 语言中延迟函数 defer 充当着 try...catch 的重任,使用起来也非常简便,然而在实际应用中,很多 gopher 并没有真正搞明白 defer.return.返回值.panic 之间的执行顺序,从而掉进坑中,今天我们就来揭开它的神秘面纱!话不多说了,来一起看看详细的介绍吧. 先来运行下面两段代码: A. 匿名返回值的情况 package main import ( "fmt&qu

  • GO语言延迟函数defer用法分析

    本文实例讲述了GO语言延迟函数defer用法.分享给大家供大家参考.具体分析如下: defer 在声明时不会立即执行,而是在函数 return 后,再按照 FILO (先进后出)的原则依次执行每一个 defer,一般用于异常处理.释放资源.清理数据.记录日志等.这有点像面向对象语言的析构函数,优雅又简洁,是 Golang 的亮点之一. 代码1:了解 defer 的执行顺序 复制代码 代码如下: package main import "fmt" func fn(n int) int {

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

    目录 什么是defer Go语言defer预计算参数 总结 什么是defer defer用来声明一个延迟函数,把这个函数放入到一个栈上, 当外部的包含方法return之前,返回参数到调用方法之前调用,也可以说是运行到最外层方法体的"}"时调用.我们经常用他来做一些资源的释放,比如关闭io操作 func doSomething(fileName string) { file,err := os.Open(fileName) if err != nil { panic(err) } def

  • 聊聊golang的defer的使用

    序 本文主要研究一下golang的defer defer return先赋值(对于命名返回值),然后执行defer,最后函数返回 defer函数调用的执行顺序与它们分别所属的defer语句的执行顺序相反 defer后面的表达式可以是func或者是method的调用,如果defer的函数为nil,则会panic 实例 实例1 // f returns 42 func f() (result int) { defer func() { // result is accessed after it w

  • golang中defer的基本使用教程

    目录 前言 1.什么是defer 2.defer的特点 3.defer什么时间执行 4.defer常见的坑 1.输出是多少? 2.输出多少 3.输出多少 4.输出什么 总结 前言 第一次看go基础语法的时候,用使用到了defer.但是一直不知道它到底是什么,有什么用途.这几天通过查询.学习.算是对defer有了一点浅显的认识. 1.什么是defer defer是go中一种延迟调用机制,defer后面的函数只有在当前函数执行完毕后才能执行,通常用于释放资源. 2.defer的特点 defer遵循先

  • 简单谈谈Golang中的字符串与字节数组

    前言 字符串是 Go 语言中最常用的基础数据类型之一,虽然字符串往往都被看做是一个整体,但是实际上字符串是一片连续的内存空间,我们也可以将它理解成一个由字符组成的数组,Go 语言中另外一个与字符串关系非常密切的类型就是字节(Byte)了,相信各位读者也都非常了解,这里也就不展开介绍. 我们在这一节中就会详细介绍这两种基本类型的实现原理以及它们的转换关系,但是这里还是会将介绍的重点主要放在字符串上,因为这是我们接触最多的一种基本类型并且后者就是一个简单的 uint8 类型,所以会给予 string

  • 简单聊聊Vue中的计算属性和属性侦听

    目录 1. 计算属性 语法:  1.简写方式: 语法:  2.完整写法: 2. 监视(侦听)属性 1. 监视属性watch: 2. 深度监视 3. 区别和原则 总结 1. 计算属性 定义 计算属性:要用的属性不存在,要通过已有属性计算得来,计算属性要有一个全新的配置项computed 对Vue来说,data里面的数据就是属性,只要Vue中的数据改变,就会重新解析模板,遇到插值语法里的方法会重新调用 原理 底层借助了Objcet.defineproperty方法提供的getter和setter.

  • 简单聊聊C++中回调函数的实现

    目录 前言 1 函数指针 2 C风格的回调函数 3 C++风格的回调函数 4 多态类型的回调函数 5 通过function和bind实现回调函数功能 6 总结 前言 回调函数其实和普通函数一样,不同的是普通函数是直接在程序中进行调用,回调函数是通过函数指针将它的地址传递给其它函数,函数执行在其它函数体执行,这个过程就叫做回调.所以,C++回调函数也并非高大上的技术,它的原理无非就是函数指针或者对象的传递. 回调函数机制: 1.定义一个函数(普通函数即可): 2.将此函数的地址注册给调用者: 3.

  • 聊聊Golang中很好用的viper配置模块

    前言 viper 支持Yaml.Json. TOML.HCL 等格式,读取非常的方便. 安装 go get github.com/spf13/viper 如果提示找不到golang.org/x/text/这个库,是因为golang.org/x/text/这个库在GitHub上托管的路径不一致. 解决办法: 可以从https://github.com/golang/text下载源码下来,然后到$GOPATH/src下面创建golang.org/x/文件夹(已存在的忽略),把压缩包的文件解压到gol

随机推荐