还可以更加深入地对源码以及汇编进行研究

作用及常见场景

defer 语句,会将其作用的语句进行延迟处理:当 defer 所属的函数执行结束并将要返回的时候,会将延迟处理的语句,按照 defer逆序 进行执行。

该语句常用于释放掉已经分配的资源,比如互斥锁的释放或者文件的关闭。也有人推荐于 recover 结合使用:

https://zhuanlan.zhihu.com/p/63354092: defer用于释放资源的话,是比自己手动释放资源的开销要大的。我认为和 recover 结合使用,即在你不知道的程序何时可能会panic的时候,引入defer+recover

语法

defer 作用的必须是函数调用语句,而不能是其他的语句,否则编译会报错。

示例

  1. /**
  2. * 代码出自 log 包源代码
  3. * 该函数使用 defer 完成了对互斥锁的释放
  4. */
  5. func (l *Logger) Flags() int {
  6. l.mu.Lock()
  7. defer l.mu.Unlock()
  8. return l.flag
  9. }

常见问题

如果让defer的函数在宿主函数的执行过程中间提前执行

产生这个问题的需求源自于:如果在函数的入口就锁住了资源,而在执行过程中已经完成了对资源的访问,宿主函数的后半段如果继续加锁,则造成了资源的浪费。

我认为这个问题的最佳解法,就是拆分成两个函数,因为宿主函数本身就不够合理,产生了逻辑上的耦合,才引发的这个问题。如果一定要写在一块,那么可以使用匿名函数,将涉及锁的部分写在匿名函数中,相当于变相地拆分了逻辑,也可以解决这个问题。

  1. // 示例
  2. func (l *Logger) Flags() int {
  3. func (){
  4. l.mu.Lock()
  5. defer l.mu.Unlock()
  6. }()
  7. return l.flag
  8. }

多个 defer 的执行顺序到底是怎样的

  1. func do() (int,error){
  2. fmt.Println("start")
  3. defer fmt.Println("defer1")
  4. defer fmt.Println("defer2")
  5. defer fmt.Println("defer3")
  6. fmt.Println("end")
  7. return fmt.Println("return")
  8. }
  9. /**
  10. * output
  11. * start
  12. * end
  13. * return
  14. * defer3
  15. * defer2
  16. * defer1
  17. */

如果 defer 的函数中有参数,其传入的时间点是什么时候

是在defer语句出现的位置,而不是其执行的时候

  1. func do() {
  2. var a int = 1
  3. defer func(a int){
  4. a++
  5. fmt.Println(a)
  6. }(a)
  7. a = 9
  8. }
  9. // output: 2

值得注意的是,这里是通过传参的方式将a传入,如果是直接在 func 中调用a这个局部变量,则输出为10