go defer详解
defer 延迟调用函数
为什么需要defer语句?
func readFileWithoutDefer(filename string) { |
readFileWithoutDefer
中需要在任何执行路径下都正确关闭文件,当函数逻辑边的复杂时,需要处理的错误情况越来越多,这时简单的清理操作就会造成之后的维护问题。
defer 语句是一个普通的函数或方法调用,只不过要在调用之前,加上defer关键字,其将函数的调用推迟,在 defer 调用的函数中执行清理操作来简化实现负,担无论包含defer语句的函数是正常退出,还是不正常,退出比如触发 panic 。都会再函数结束后,调用 defer 语句。
defer没有次数限制,执行时按照调用defer顺序的倒序执行
defer 中值的确定
通常使用闭包来写defer,这就涉及到defer执行的时候是怎么确定里面的值的,主要有以下原则
- 作为参数传入:定义defer时确定
- 作为闭包引入:执行defer时确定
// 作为参数传入:定义defer时确定 |
使用场景
资源释放、错误处理
defer 通常成对出现:连接建立和关闭,锁的获取和释放,文件的打开和关闭
调试函数
defer 还可以用来调试复杂函数,统计函数的运行时间,这段代码很巧妙,引用自《Go程序设计语言》
func trace(msg string) func() { |
修改返回值
在defer中只能修改具名返回值,如果是指针,则可以修改指针指向的值
// DeferReturn 闭包引用 |
func TestDeferReturn(t *testing.T) { |
DeferReturn()
可以看到,即使地址一样,闭包引入的值不能修改;DeferReturnV2()
中的代码,演示了可以通过修改指针指向的值来修改,但本质上a
是无法修改的
实现机制
堆上分配
整个defer直接分配到堆上
缺点:被GC管理
栈上分配
整个defer分配到goroutine上,不需要被GC管理,相对于分配在栈上,性能提升了30%,而defer具体在堆上分配还是在栈上分配,取决于逃逸分析。
开放编码
启用内联的优化,直观上理解就是把defer内容放到函数的最后,编译器帮我把编码转移到了函数的最后
启用条件:
- defer的数量 <= 8; 用了一个byte来记录哪些defer需要执行
- defer关键字不能再循环中执行:;编译时不知道有多少个defer
- return语句个数 * defer个数 <= 15;硬性规定
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Xinfinty!