还可以更加深入地对源码以及汇编进行研究
作用及常见场景
defer
语句,会将其作用的语句进行延迟处理:当 defer
所属的函数执行结束并将要返回的时候,会将延迟处理的语句,按照 defer
的 逆序 进行执行。
该语句常用于释放掉已经分配的资源,比如互斥锁的释放或者文件的关闭。也有人推荐于 recover
结合使用:
https://zhuanlan.zhihu.com/p/63354092: defer用于释放资源的话,是比自己手动释放资源的开销要大的。我认为和 recover 结合使用,即在你不知道的程序何时可能会panic的时候,引入defer+recover
补充:经后续学习,与recover搭配确实是一个好的实践
语法
defer
作用的必须是函数调用语句,而不能是其他的语句,否则编译会报错。
示例
/**
* 代码出自 log 包源代码
* 该函数使用 defer 完成了对互斥锁的释放
*/
func (l *Logger) Flags() int {
l.mu.Lock()
defer l.mu.Unlock()
return l.flag
}
常见问题
如果让defer的函数在宿主函数的执行过程中间提前执行
产生这个问题的需求源自于:如果在函数的入口就锁住了资源,而在执行过程中已经完成了对资源的访问,宿主函数的后半段如果继续加锁,则造成了资源的浪费。
我认为这个问题的最佳解法,就是拆分成两个函数,因为宿主函数本身就不够合理,产生了逻辑上的耦合,才引发的这个问题。如果一定要写在一块,那么可以使用匿名函数,将涉及锁的部分写在匿名函数中,相当于变相地拆分了逻辑,也可以解决这个问题。
// 示例
func (l *Logger) Flags() int {
func (){
l.mu.Lock()
defer l.mu.Unlock()
}()
return l.flag
}
多个 defer
的执行顺序到底是怎样的
func do() (int,error){
fmt.Println("start")
defer fmt.Println("defer1")
defer fmt.Println("defer2")
defer fmt.Println("defer3")
fmt.Println("end")
return fmt.Println("return")
}
/**
* output
* start
* end
* return
* defer3
* defer2
* defer1
*/
如果 defer
的函数中有参数,其传入的时间点是什么时候
是在defer语句出现的位置,而不是其执行的时候
func do() {
var a int = 1
defer func(a int){
a++
fmt.Println(a)
}(a)
a = 9
}
// output: 2
值得注意的是,这里是通过传参的方式将a传入,如果是直接在 func 中调用a这个局部变量,则输出为10