Go defer用法
defer概览
- defer是go语言里的一个关键字,在 函数内部使用;
- defer关键字后面跟一个 函数或匿名函数;
defer用法
- 执行一些资源的收尾工作,如 关闭数据库连接,关闭文件描述符,释放资源等等;
- 结合recover()函数使用,防止函数内部的异常导致整个程序停止;
- defer在遇到panic后,仍然会执行(defer语句要在panic之前编译),从而保证即使出错也能进行对应的错误处理;
函数中多个defer间存储方式及运行顺序
defer数据结构
type _defer struct {siz int32started boolopenDefer boolsp uintptrpc uintptrfn *funcval_panic *_paniclink *_defer
}
- _defer结构体中的link字段 指向下一个defer结构体地址的指针;
- _defer结构体是 延迟调用链表中的一个元素,所有的结构体都是通过 link字段串联成链表;
- 使用链表方式存储,
- 代码从上到下编译,遇到的 defer都会放到链表头部,后面执行的执行按照链表顺序从头到尾执行
- defer编译及执行顺序 就是 栈的入栈出栈的顺序
defer必须要了解的特性
多defer执行顺序
结论
多defer执行顺序是 最后编译的先执行;
defer后面的函数的参数值的预计算
结论
defer在编译时,会对后面跟着的函数的参数值进行预计算;
也就是 编译器编译到此行时,会立刻确定 defer后面跟着函数的参数值,并且是 值传递方式,不是引用传递方式; 这样后续无论 这个参数对应的变量 值怎么改变,都和defer后面的函数无关;
所以 即使 参数 传了一个 函数,也会先计算函数的值 作为参数 进行值引用;
示例代码
defer后面跟的是系统自带的 fmt.Println函数
package mainimport "fmt"func A(v int) int {fmt.Println("A函数,入参为", v)v += 1return v
}func UseA(v int) int {defer fmt.Println("defer执行结果", A(v)) // A(v)的值:2 对应解析里的 "即使 参数 传了一个 函数,也会先计算函数的值 作为参数 进行值引用; "defer fmt.Println("defer执行结果", v) //v的值:1 对应解析里的 "后续无论 这个参数对应的变量 值怎么改变,都和defer后面的函数无关;"fmt.Println("UseA执行")v += 5fmt.Println("UseA执行return代码前的最后一行")return v
}func main() {fmt.Println("UseA执行结果", UseA(1)) //6return
}
代码执行结果
defer与return关键字执行顺序
结论
- defer会在return关键字之后, 在返回给调用方前执行;
示例代码
package mainimport "fmt"func A(v int) int {fmt.Println("A函数,入参为", v)v += 1return v
}func UseOtherA(v int) int {defer fmt.Println("defer执行")return A(v)
}func main() {fmt.Println("UseOtherA执行结果",UseOtherA(1))return
}
代码执行结果
defer对 函数的返回值 是否定义变量名的影响
示例代码
package mainimport "fmt"func A(v int) int {fmt.Println("A函数,入参为", v)v += 1return v
}
func B(v int) (result int) {result = vdefer func() {result = A(v)}()return
}func C(v int) int {defer func() {v = A(v)}()return v
}
func main() {fmt.Println("B执行结果", B(1))fmt.Println("C执行结果", C(1))return
}
代码执行结果
结论
- 定义返回值变量名 的函数,在返回给函数调用方前,这个变量的值都是可以修改的;
- 未定义返回值变量名 的函数, 如上示例的C函数在 return语句执行时,其实是 将v变量 赋值给一个未定义名字的隐藏变量,来完成值传递, 所以后续对v变量的操作对 返回给函数调用结果无任何影响; 示意如下
defer遇到panic执行情况
结论
- panic触发后,函数内的后续代码不再执行,在panic之前编译的defer仍然会执行;
示例代码
package mainimport "fmt"func PanicT() {defer func() {if err := recover(); err != nil {fmt.Println("执行recover()方法,尝试恢复程序,错误内容为:", err)}}()defer fmt.Println("defer1")defer fmt.Println("defer2")panic("手动触发panic")defer fmt.Println("defer3,执行不到")
}func main() {PanicT()return
}
运行结果
defer中包含panic
结论
- panic会被覆盖,只保留最新的panic;
- 如: 函数中的panic会被defer的panic覆盖;
- 如: 多个defer都有panic,只有最后执行的defer的panic会保留;
- 其实 defer 后面也是普通函数,那么普通函数遇到panic就会停止运行,执行后续defer的函数;
代码示例
package mainimport "fmt"func PanicMany() {defer func() {if err := recover(); err != nil {fmt.Println("执行recover()方法,尝试恢复程序,错误内容为:", err)}}()defer func() {fmt.Println("defer1执行")panic("defer1手动触发panic")fmt.Println("defer1执行不到此处")}()defer func() {fmt.Println("defer2执行")panic("defer2手动触发panic")fmt.Println("defer2执行不到此处")}()panic("手动触发panic")defer fmt.Println("defer3,执行不到")
}func main() {PanicMany()return
}
代码执行结果
知识点训练
func main() {fmt.Println(test1())fmt.Println(test2())fmt.Println(test3())fmt.Println(test4())
}
func test1() (v int) {defer fmt.Println(v) //0return v //0
}func test2() (v int) {defer func() {fmt.Println(v) //3}()return 3 //3
}func test3() (v int) {defer func() {fmt.Println(v)}() //4defer fmt.Println(v) //0defer func(v int) { fmt.Println(v) }(v) //0v = 3return 4 //4
}func test4() (v int) {defer func(n int) {fmt.Println(n) //0}(v)return 5 //5
}
总结
- 对于defer执行结果的准确预测, 要了解函数的参数传递方式,函数的返回值是否定义变量名时 编译器的执行过程 等额外的知识点;
- defer代码编写时经常遇到,常用于 异常捕捉,资源释放等收尾工作;
Go defer用法相关推荐
- Go 延迟调用 defer 用法详解
引子 package counterimport ("log""sync" )type Counter struct {mu *sync.MutexValue ...
- Go语言 defer
引言 Go 语言中的 defer 语句是 UNIX 之父 Ken Thompson 大神发明的,是完全正交的设计. 也正因为 Go 语言遵循的是正交的设计, 所以才有了: "少是指数级的多/ ...
- c语言memcpy是什么,C语言memcpy函数的用法
介绍 memcpy是memory copy的缩写,意为内存复制,在写C语言程序的时候,我们常常会用到它.它的函原型如下: void *memcpy(void *dest, const void *sr ...
- 秋招面试准备 JS1
1几个很实用的BOM属性对象方法 参考27.BOM的属性对象方法_y_xiuzhu的博客-CSDN博客 1 BOM的属性对象方法 BOM的对象用于访问浏览器的功能,提供了当前窗口中加载的文档有关的信息 ...
- RxJava 2.x入门教程
前言 首先来说一下rxjava1和rxjava2的区别吧,附带一些RxJava 1升级到RxJava 2过程中踩过的一些"坑",RxJava 对大家而言肯定不陌生,其受欢迎程度不言 ...
- Golang 基础之基础语法梳理 (一)
大家好,今天将梳理出的 Go语言基础语法内容,分享给大家. 请多多指教,谢谢. 本次<Go语言基础语法内容>共分为三个章节,本文为第一章节 Golang 基础之基础语法梳理 (一) Gol ...
- Go语言实现HTTP压测工具(2)——Golang语言基础学习和使用
文章目录 0 前言 1. 数据类型 2. 函数 3. 控制结构 3.1 for range用法 3.2 defer用法 4. 数组.切片(Slice)与映射(Map) 5. 结构体(Struct)与方 ...
- Go语言中 defer 的用法
文章目录 Go语言中 defer 的用法 一.defer触发时机 二.defer执行逻辑 1. 多个defer语句按先进后出的方式执行 2.defer声明时,对应的参数会实时解析 3.defer.re ...
- 关于defer 的用法
要是以前,我铁定整天到处找教程看,光说不练,现在觉悟了,看教程看得最多,不一定能看完,看完了不一定能比作者更明白,看明白了不一定能用得好.所以看教程其实好处不大,只能作为小小的参考.很多东西看别人的始 ...
最新文章
- 查询远程或本地计算机的登录账户
- windows phone7---MVVM模式
- linux之vim操作快速跳到下一个空格和上一个空格命令
- CSDN博客标题和目录的一点思考
- 菜鸟教程c语言题目,C 练习实例40
- Ubuntu安装tensorflow报错:tensorflow-xx.whl not a supported wheel on this platform
- 最短路径之Dijkstra算法和Floyd-Warshall算法
- load runner
- 采用nettcp绑定的wcf宿主到iis7
- 邮局只能寄指定大小的箱子
- 跟小丸子学基础口语16-20
- Android屏幕适配(SmallestWidth适配 sw限定符)最新步骤解析
- 计算机在中医方剂中的应用,计算机中医应用(精).docx
- PIC单片机入门教程(三)—— 安装编译器(MPLAB XC Compilers)
- 基于BP神经网络PID控制+Simulink仿真
- 大规模定制生产模式及其关键技术
- 圣诞礼物|2020年送这些礼物你就能拥有一个程序员男朋友
- diff比较两个目录时,如何略过特定目录或文件
- 5个国内优秀网站设计案例分享
- 【前端技术】一篇文章搞掂:WeX5