当前位置:网站首页>(十)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")
}
边栏推荐
- 哪些字符串会被FastJson解析为null呢?
- Feign call exception [running, pool size = 10, active threads = 10, queued tasks = 0, completed tasks = n]
- Linux操作系统(Centos7)安装MySQL
- OSS direct upload rails service practice
- Data can't lie. Plato farm is the leader of the meta universe
- Function introduction and description of @jsontype annotation in fastjson
- Today, I want to talk about the data types of MySQL database
- Plato farm - a farm meta universe game with Plato as the goal
- 总线相关概念集合
- Introduction to SD card (based on spec3.0)
猜你喜欢

SQL server, MySQL master-slave construction, EF core read-write separation code implementation

多线程一定能优化程序性能吗?

超级原始人系列盲盒即将上线,PlatoFarm赋能超多权益

The secret behind three salary increases a year

时序分析41 - 时序预测 TBATS模型
JWT 登录认证 + Token 自动续期方案,写得太好了!

Real time editor of MATLAB

Linux操作系统(Centos7)安装MySQL

MySQL master-slave architecture. After the master database is suspended and restarted, how can the slave database automatically connect to the master database

Learn a hammer.Net zero foundation reverse tutorial lesson 3 (shell and homework)
随机推荐
Some problems about CLR GC tuning
Word segmentation results of ES query index fields
PHP 获取接口的方式
领域事件和集成事件没那么高大上
[ESP32][esp-idf] esp32s3快速搭建LVGLV7.9
Create SSL certificate using OpenSSL
Translation recommendation | debugging bookkeeper protocol - unbounded ledger
redis的基础知识
j s的数组方法,循环
【MySQL】Got an error reading communication packets
哪些字符串会被FastJson解析为null呢?
pycharm使用conda调用远程服务器
为报复公司解雇,我更改了项目的所有代码注释!
Method parameter transfer mechanism of C #
The high temperature continues, and public transport enterprises carry out special safety training
Introduction to evaluatorfilter
Introduction to timebasedrollingpolicy
数据库高级技术学习笔记1--Oracle部署和PL/SQL综述
数据库高级学习笔记--存储结构
Arouter source code analysis (III)