关于golang:Go-defer-特性和使用场景

7次阅读

共计 1649 个字符,预计需要花费 5 分钟才能阅读完成。

golang 的 defer 语句用于提早调用。defer 会在以后函数返回之前执行 defer 注册的函数。比方 defer func_defer() 这样语句会让你注册一个函数变量到 defer 的全局链表中,在 defer 语句所在的函数退出之前调用。

defer 能够代替其它语言中 try…catch… 语句,也能够用来解决开释资源等收尾操作,比方敞开文件句柄、敞开数据库连贯等。defer 还能用于 panic 的 recovery。

1. defer 的个性

咱们先深刻的分析下 defer 具备的个性,知其然也。这些个性是须要咱们记住的特点,能力更好的了解 defer 应用的场景。

1) 提早调用

package main

func main() {defer println("--- defer ---")
    println("--- end ---")
}

运行后果:

--- end ---
--- defer ---

defer 会在 main 函数所有语句之后,return 之前时候调用。外围要点:

提早调用:defer 语句自身尽管是 main 的第一行,然而 println(“— end —“) 先打印的;
defer 关键字肯定是处于函数上下文:defer 必须放在函数外部;

2) LIFO

一个函数中含有有多个 defer,调用程序采纳压栈式执行,后入先出(LIFO)。

package main

import ("strconv")

func main() {
    for i := 1; i <= 3; i++ {defer println("defer -->" + strconv.Itoa(i))
    }
    println("--- end ---")
}

压栈式执行,也就是说先注册的函数后调用。如上,咱们注册的程序式 1,2,3,最初打印“— end —”,所以执行的后果天然是反着来的,程序输入:

--- end ---
defer -->3
defer -->2
defer -->1

3) 作用域

defer 只会和 defer 语句所在的特定函数绑定在一起,作用域也只在这个函数。从语法上来讲,defer 语句也肯定要在函数内,否则会报告语法错误。

package main

func main() {func() {defer println("--- defer ---")
    }()
    println("--- end ---")
}

如上,defer 处于一个匿名函数中,就 main 函数自身来讲,匿名函数 fun(){}() 先调用且返回,而后再调用 println(“— end —“),所以程序输入天然是:

--- defer ---
--- end ---

4) 异样场景

这个是十分重要的个性:panic 也能执行。golang 不激励异样的编程模式,然而却也留了 panic-recover 这个异样和捕获异样的机制。所以 defer 机制就显得尤为重要,甚至能够说是必不可少的。因为你没有一个忽视异样,永保调用的 defer 机制,很有可能就会产生各种资源泄露,死锁等场景。为什么?因为产生了 panic 却不代表过程肯定会挂掉,很有可能被外层 recover 住。

package main

func main() {defer func() {if e := recover(); e != nil {println("--- defer ---")
        }
    }()
    panic("throw panic")
}

如上,main 函数注册一个 defer,且稍后被动触发 panic,main 函数退出之际就会调用 defer 注册的匿名函数。再提一点,这里其实有两个要点:

  1. defer 在 panic 异样场景也能确保调用;
  2. recover 必须和 defer 联合才有意义;

3. 总结

  1. defer 其实并不是 golang 独创,是多种高级语言的独特抉择;
  2. defer 最重要的一个特点就是忽视异样可执行,这个是 golang 在提供了 panic-recover 机制之后必须做的弥补机制;
  3. defer 的作用域存在于函数,defer 也只有和函数联合才有意义;
  4. defer 容许你把配套的两个行为代码放在最近相邻的两行,比方创立 & 开释资源、加锁 & 开释锁等,使得代码更易读,编程体验优良。

参考资料

  1. go 语言编程
  2. 编程宝库
正文完
 0