当前位置:网站首页>(十)defer关键字
(十)defer关键字
2022-07-28 09:39:00 【二进制杯莫停】
(十)defer关键字
golang中的defer关键字用来声明一个延迟函数,该函数会放在一个列表中,在defer语句的外层函数返回之前系统会执行该延迟函数。defer特点有:
- 函数返回之前执行
- 可以放在函数中任意位置
- 可以同时设置多个defer函数,多个defer函数执行遵循FILO顺序
- defer函数的传入参数在定义时就已经明确
- 可以修改函数中的命名返回值 用于文件资源,锁资源、数据库连接等释放和关闭
- 和recover一起处理panic
1. defer会在函数返回之前执行
当程序执行一个函数时候,会将函数的上下文(输入参数,返回值,输出参数等信息)作为栈帧放在程序内存的栈中,当函数执行完成之后,设置返回值并返回,此时栈帧退出栈,函数才真正完成执行。
defer语句函数会在函数返回之前执行,下面程序将会依次输出B A:
func main() {
defer fmt.Println("A")
fmt.Println("B")
}
2. defer可以放在函数中任意位置
func main() {
fmt.Println("A")
defer fmt.Println("B")
fmt.Println("C")
}
上面程序将会依次输出A C B
注意:
- defer语句一定要在函数return语句之前,这样才能生效。下面程序将会只输出A
func main() {
fmt.Println("A")
return
fmt.Println("B")
}
- 在调用os.Exit时候,defer不会执行。下面程序只会输出B
func main() {
defer fmt.Println("A")
fmt.Println("B")
os.Exit(0)
}
3. 可以同时设置多个defer函数
可以设置多个defer函数,多个defer函数执行遵循FILO顺序,下面程序将依次输出B D C A
func main() {
defer fmt.Println("A")
fmt.Println("B")
defer fmt.Println("C")
fmt.Println("D")
}
我们来看下多个defer嵌套情况:
func main() {
fmt.Println("A")
defer func() {
fmt.Println("B")
defer fmt.Println("C")
fmt.Println("D")
}()
defer fmt.Println("E")
fmt.Println("F")
}
上面程序将依次输出: A F E B D C
defer语句内部实现形式是一个结构体:
# 位于/usr/lib/go/src/runtime/runtime2.go#784
type _defer struct {
...
sp uintptr // 函数栈指针,sp是stack pointor单词首字母缩写
pc uintptr //程序计数器, pc是program counter单词首字母缩写
fn *funcval // 函数地址,执行defer函数
_panic *_panic // 指向最近一次panic
link *_defer // 指向下一个_defer结构
...
}
defer内部实现是一个链表,链表元素类型是_defer结构体,其中的link字段指向下一个_defer地址,当定义一个defer语句时候,系统内部会将defer函数转换成_defer结构体,并放在链表头部,最后执行时候,系统会从链表头部开始依次执行,这也就是多个defer的执行顺序是First In Last out的原因。
4. defer函数的传入参数在定义时就已经明确
defer函数的传入参数在定义时就已经明确,不论传入的参数是变量、表达式、函数语句,都会先计算出计算出实参结果,再随defer语句入栈
func main() {
i := 1
defer fmt.Println(i)
i++
return
}
上面程序输出1,而不是2
注意:
当defer类似闭包使用时候,访问的总是循环中最后一个值
func main() {
for i:=0; i<5; i++ {
defer func() {
fmt.Println(i)
}()
}
}
上面程序连续输出5个5
解决办法可将值传入闭包函数中,此时defer函数入栈时候,不光入栈地址,还会记录传入参数,等到执行的时候也就打印输出入栈时候的值
func main() {
for i:=0; i<5; i++ {
defer func(i int) {
fmt.Println(i)
}(i)
}
}
此时依次输出4 3 2 1 0
5. 多用于文件资源关闭,数据库等连接关闭
处理资源释放回收
通过defer我们可以简洁优雅处理资源回收问题,避免复杂的代码逻辑情况下,遗漏忽视相关的资源回收问题。
我们看下下面的代码,目的是复制文件内容到一个新文件
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
dst, err := os.Create(dstName)
if err != nil {
return
}
written, err = io.Copy(dst, src)
dst.Close()
src.Close()
return
}
上面代码是存在bug的。当文件创建失败的时候,直接返回了,却没有对打开的文件资源关闭回收。
通过defer我们可以保证始终能够正确的关闭资源, 并且处理逻辑简单优雅。
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()
return io.Copy(dst, src)
}
和recover一起处理panic
recover用户捕获panic异常,panic用于抛出异常。recover需要放在defer语句中,否则无法捕获到一次
下面这个例子将会捕获到panic, 并且输出panic信息
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
}
}()
panic("it is panic")
}
多个恐慌同时发生时候,只会捕获第一个恐慌
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
}
}()
panic("it is panic")
panic("it is another panic")
}
边栏推荐
- C# 读写文件从用户态切到内核态,到底是个什么流程?
- 3分钟带你了解微信小程序开发
- PHP Basics
- Business visualization - make your flowchart'run'(4. Actual business scenario test)
- Set of bus related concepts
- 【MySQL】Got an error reading communication packets
- 时序分析41 - 时序预测 TBATS模型
- 高温天气筑牢安全生产防线,广州海珠区开展加油站应急演练
- C form application uses object binding DataGridView data binding
- 【MySQL】MySQL错误“ERROR 2006 (HY000):MySQL server has gone away”
猜你喜欢

Learn a hammer.Net zero foundation reverse tutorial lesson 3 (shell and homework)

初学C#必须要掌握的基础例题

Buckle 376 swing sequence greedy

这种动态规划你见过吗——状态机动态规划之股票问题(中)

C countdown tool

The high temperature continues, and public transport enterprises carry out special safety training

MySQL master-slave architecture. After the master database is suspended and restarted, how can the slave database automatically connect to the master database
JWT 登录认证 + Token 自动续期方案,写得太好了!

WPF布局之控件随着窗口等比放大缩小,适应多分辨率满屏填充应用

高温持续,公交企业开展安全专项培训
随机推荐
Introduction to timebasedrollingpolicy
In hot weather, the line of defense for safe production was strengthened, and Guangzhou Haizhu District carried out emergency drills for gas stations
ConsoleAppender简介说明
Plato Farm-以柏拉图为目标的农场元宇宙游戏
PHP 基础
Read Plato farm's eplato and the reason for its high premium
Introduction to evaluatorfilter
SQL server, MySQL master-slave construction, EF core read-write separation code implementation
Pycharm uses CONDA to call the remote server
Opencv installation configuration test
哪些字符串会被FastJson解析为null呢?
PHP connection MySQL native code
Set of bus related concepts
Arouter source code analysis (I)
软件测试与质量学习笔记1---黑盒测试
Edge团队详解如何通过磁盘缓存压缩技术提升综合性能体验
MySQL master-slave architecture. After the master database is suspended and restarted, how can the slave database automatically connect to the master database
web之圣杯和双飞翼布局、float、clear、both
Some problems about CLR GC tuning
Seektiger eco pass STI new progress, log in to ZB on April 14