go defer简介
思考
开始之前,先考虑下下面的代码的执行结果:
package main
import "fmt"
func test() int {
i := 0
defer func() {
fmt.Println("defer1")
}()
defer func() {
i += 1
fmt.Println("defer2")
}()
return i
}
func main() {
fmt.Println("return", test())
}
defer介绍
defer 是 Go 编程语言中的一个关键字,用于在函数执行结束后延迟执行指定的函数调用。defer 的使用非常灵活,它通常用于执行一些清理操作、资源释放、日志记录等任务。以下是对 defer 的详细介绍:
defer的语法:defer后面跟随一个函数调用,该函数会在包含defer语句的函数执行完毕后被调用。- 语法示例:
defer someFunction()
执行时机:
defer函数调用会在包含defer语句的函数返回之前执行,即使在函数中间有return语句也是如此。- 这确保了
defer中的操作在函数结束时始终执行,无论函数是正常返回还是出现异常。
多个
defer语句:- 一个函数可以包含多个
defer语句,它们会以后进先出(LIFO)的顺序执行。 - 这意味着最后一个出现的
defer语句会最先执行,而最先出现的defer语句会最后执行。
- 一个函数可以包含多个
常见用途:
- 资源释放:
defer常用于关闭文件、释放锁、释放内存等资源管理任务,确保资源在函数结束时得到正确释放。 - 错误处理:
defer可以用于记录错误日志或执行清理操作,以确保即使发生错误,资源也能得到释放。 - 跟踪代码执行:
defer还可以用于记录函数的执行情况,以进行性能分析或跟踪代码路径。
- 资源释放:
示例:
下面是一个使用defer的示例,演示了文件的打开和关闭操作:func readFile(filename string) error {
file, err := os.Open(filename)
if err != nil {
return err
}
defer file.Close() // 确保文件在函数返回前关闭 // 文件操作... return nil
}
注意事项:
defer不仅用于函数的返回,还可以用于方法(类似于面向对象编程中的析构函数)。defer中的参数会在defer语句执行时被求值,因此如果你有多个defer语句使用相同的参数,它们会被依次求值。- 在某些情况下,要特别小心
defer中的闭包,以避免出现意外的行为。
defer执行时机
defer 语句中的函数调用会在包含 defer 语句的函数返回之前执行。无论函数是正常返回还是在执行中发生了 panic,defer 中的函数都会按照后进先出(LIFO)的顺序执行。这确保了在函数结束时进行清理和释放资源,以及在函数执行期间处理错误或日志记录等任务。
以下是关于 defer 执行时机的详细解释:
正常返回时的
defer执行:- 在函数执行过程中,当遇到
defer语句时,不会立即执行defer中的函数调用,而是将它们压入一个栈中,以便在函数返回时执行。 - 当函数执行完毕并准备返回时,栈中的
defer函数调用会按照后进先出的顺序执行,确保最后一个defer最先执行。
- 在函数执行过程中,当遇到
发生 panic 时的
defer执行:- 如果函数在执行中发生 panic(异常),同样会执行
defer中的函数,然后再传播 panic,这允许在 panic 后执行清理操作。 - 这可以用来释放资源、记录错误信息、关闭连接等。
- 如果函数在执行中发生 panic(异常),同样会执行
下面是一个示例,说明了 defer 的执行时机:
func exampleFunction() {
defer fmt.Println("Deferred 1")
defer fmt.Println("Deferred 2")
fmt.Println("Function body")
panic("Something went wrong")
}
func main() {
exampleFunction()
}
在这个示例中,exampleFunction 包含两个 defer 语句和一个 panic。当 exampleFunction 调用时,它首先打印 "Function body",然后执行 defer 中的函数。在 panic 发生后,defer 语句中的函数会按照后进先出的顺序执行。所以,main 函数的输出将是:
Function body
Deferred 2
Deferred 1
panic: Something went wrong
正如示例所示,defer 中的函数在函数返回之前或在 panic 发生后都会执行,这使得它在资源管理和错误处理方面非常有用。
结束
现在回到最开始的问题,在上面的代码中,test 函数包含两个 defer 语句,以及一个 return 语句。在 main 函数中,我们调用 test 并输出其返回值。让我们来解释每一步并分析输出的结果:
i初始化为0。- 第一个
defer语句中的匿名函数只是打印 "defer1",不对i进行任何修改。 - 第二个
defer语句中的匿名函数增加了i的值,然后打印 "defer2"。
现在,让我们分析 test 函数的执行流程:
i初始化为0。- 第一个
defer语句注册的函数(打印 "defer1")会在函数返回之前执行,但它没有影响i的值。 - 接下来,第二个
defer语句注册的函数(增加i的值并打印 "defer2")也会在函数返回之前执行,但在执行时,i的值仍然为0。 return i语句返回0。
因此,test 函数返回 0,但在执行过程中,两个 defer 函数都被执行,按照注册的顺序分别打印 "defer1" 和 "defer2"。
在 main 函数中,我们调用 test 并输出其返回值,因此最终的输出是:
defer2
defer1
return 0
这是因为 defer2 和 defer1 的输出分别在 test 函数调用结束之前执行,而 return 0 的结果在函数返回后被 main 函数输出。
声明:本作品采用署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)进行许可,使用时请注明出处。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 恋水无意
go defer简介的更多相关文章
- GO 语言简介(网摘)
GO 语言简介 原文出处:[陈皓 coolshell] Hello World 文件名 HELLO.GO package main //声明本文件的package名 import "fmt& ...
- Bootstrap 简介: 创建响应式、移动项目的工具
原文链接: Introduction to Bootstrap: A Tool for Building Responsive, Mobile-First Projects 下载: 示例代码Boots ...
- Containerd 简介
我们可以把 docker 抽象为下图所示的结构(此图来自互联网): 从图中可以看出,docker 对容器的管理和操作基本都是通过 containerd 完成的. 那么,containerd 是什么呢? ...
- JavaScript简介与使用方法
1.JavaScript简介 1.1.JavaScript简史 最初:网络通信很慢,网页上的数据要传送到数据库验证,然后再返回错误结果,找客观过程要等很久,于是,网景公司开发出一门新语言,当时Java ...
- Go 语言简介(下)— 特性
希望你看到这篇文章的时候还是在公交车和地铁上正在上下班的时间,我希望我的这篇文章可以让你利用这段时间了解一门语言.当然,希望你不会因为看我的文章而错过站.呵呵. 如果你还不了解Go语言的语法,还请你移 ...
- 【JavaScript】简介、<Script>标签及基本概念
一.前言 时光荏苒,岁月匆匆.今年年初进入数据平台部门转型做Web平台.要想搞好前端肯定要学好JavaScript,于是准备抓上一俩本书从基础学起. 二.内容 简介 JavaScript是 ...
- Redis和Memcache和MongoDB简介及区别分析(整理)
Redis和Memcache 一.Redis简介 Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.从2010年 ...
- Go之Casbin简介,安装,模型,存储,函数
简介 Casbin是一个强大的,高效的开源访问控制框架,其权限管理机制支持多种访问控制模型 支持编程语言 不同语言中支持的特性 我们一直致力于让 Casbin 在不同的编程语言中拥有相同的特性. 但是 ...
- 2. Go中defer使用注意事项
1. 简介 defer 会在当前函数返回前执行传入的函数,它会经常被用于关闭文件描述符.关闭数据库连接以及解锁资源. 理解这句话主要在三个方面: 当前函数 返回前执行,当然函数可能没有返回值 传入的函 ...
- ASP.NET Core 1.1 简介
ASP.NET Core 1.1 于2016年11月16日发布.这个版本包括许多伟大的新功能以及许多错误修复和一般的增强.这个版本包含了多个新的中间件组件.针对Windows的WebListener服 ...
随机推荐
- 记一次 .NET 某企业采购平台 崩溃分析
一:背景 1. 讲故事 前段时间有个朋友找到我,说他们的程序有偶发崩溃的情况,让我帮忙看下怎么回事,针对这种 crash 的程序,用 AEDebug 的方式抓取一个便知,有了 dump 之后接下来就可 ...
- 快上车,搭乘HUAWEI HiCar驶向未来
HUAWEI HiCar(以下简称HiCar)是华为提供的人-车-家全场景智慧互联解决方案,连接手机与车辆,充分发挥各自的优势属性,将手机的应用/服务生态延伸进车辆,实现以手机为核心的全场景体验.消费 ...
- 如何构建高效、可观的系统「GitHub 热点速览」
经典老项目 system-design 教你如何设计一个健壮的系统,新项目 noodle 教你如何提升教育效率,而后者甚至单日获得了 1,600 star,刚开源就获得了 6k+ 的 star. 除了 ...
- .NET表达式树
IQueryable/IQueryable 和表达式树 IQueryable有两个组件 Expression:当前查询的组件的与语言和数据源无关的表示形式,以表达式树的形式表示. Provider:L ...
- you-get的使用
转载自: 利用Python下载:You-Get的安装及使用方法 - 宁佳兵 - 博客园 宁佳兵 所谓的光辉岁月,并不是后来闪耀的日子,而是无人问津时,对梦想的偏执. 博客园 首页 标签 GitHu ...
- Flask结合gunicorn和nginx反向代理的生产环境部署及踩坑记录
前言 之前自己写的flask使用gunicorn上线生产环境没有什么问题,但是最近搭建了一个现成的flask项目,当使用python直接运行时不会有问题,而使用gunicorn时则会出现一些问题. 部 ...
- SAS - PROC FCMP
PROC FCMP 概述 PROC FCMP 可用于自定义函数(funcion)和子程序(subroutines).自定义函数和子程序的名称的最大长度为 32,长度超过 32 的名称虽然可以定义,但无 ...
- 一个Web项目实现多个数据库存储数据并相互切换
1.使用场景 多数据源使用场景一般为: 主从数据库切换 读写分离 兼容旧库 2.具体实现 实现原理 Spring2.x的版本中采用Proxy模式,就是在方案中实现一个虚拟的数据源,并且用它来封装数据源 ...
- Linux 软件包:lvm
lvm LVM是 Logical Volume Manager(逻辑卷管理)的简写,它是Linux环境下对磁盘分区进行管理的一种机制. Linux用户安装Linux操作系统时遇到的一个常见的难以决定的 ...
- 关于python pycharm中输出的内容不全的解决办法
import pandas as pd #设置显示的最大列.宽等参数,消除打印不完全中间的省略号 pd.set_option("display.width",1000) #加了这一 ...