Golang 語言中的 defer 怎麼使用?
01
介紹
在 Golang 語言中,我們可以在函數(自定義和部分內置)或方法中使用 defer 關鍵字註冊延遲調用(一個或多個),多個延遲調用的執行順序是先進後出(FILO)。並且不會受到函數執行結束退出,顯式調用 return 和主動(或被動)觸發 panic 的影響,註冊成功的所有延遲調用都會被執行,除非 defer 註冊在 return 之後或者函數(或方法)調用 os.Exit(1)
。
defer 註冊多個延遲調用,執行順序是先進後出(FILO)。
示例代碼:
func main () {
defer func() {
fmt.Println("A")
}()
defer func() {
fmt.Println("B")
}()
fmt.Println("main goroutine run over")
// panic("this is a panic example")
// return
}
defer 如果定義在 return 之後,它等於 defer 沒有註冊,將不會執行。
示例代碼:
func main () {
fmt.Println("main")
return
defer func() {
fmt.Println("A")
}()
}
defer 所在的函數或方法中,如果調用 os.Exit(1)
,defer 即便註冊,也不會執行。
示例代碼:
func main () {
defer func() {
fmt.Println("A")
}()
fmt.Println("main")
os.Exit(1)
}
defer 必須在函數和方法中才可以使用,並且 defer 後面必須是函數(自定義和部分內置函數)或方法,defer 函數的實參是值拷貝。
示例代碼
func main () {
a := 0
defer func(num int) {
fmt.Println("defer func()", num)
}(a)
a++
fmt.Println(a)
}
02
使用場景
使用關鍵字 defer 註冊的函數(自定義和部分內置)或方法,因爲不會受到函數執行結束,顯式調用 return 和主動(或被動)觸發 panic 的影響,通常會用於防止忘記釋放資源和捕獲 panic(同一 goroutine 中) 防止應用程序崩潰退出的應用場景。
示例代碼:
func main () {
f, err := os.OpenFile("text.txt", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755)
if err != nil {
fmt.Println(err)
}
defer f.Close()
n, err := f.WriteString("this is a text file\t")
if err != nil {
fmt.Println(err)
}
fmt.Println(n)
}
閱讀上面這段代碼,我們使用 defer 延遲調用釋放資源,防止忘記釋放資源(關閉文件或解鎖),通常 defer 會放在錯誤檢查之後。
示例代碼:
func main () {
defer func() {
if err := recover(); err != nil {
fmt.Println("this is a panic" )
}
}()
panic("this is a test panic")
fmt.Println("main")
}
閱讀上面這段代碼,我們使用 defer 配合 recover 函數,用於攔截 panic(同一 goroutine 中),防止程序崩潰退出。
03
注意事項
雖然使用 defer 具有可以用於防止忘記釋放資源和攔截 panic(同一 goroutine 中)防止應用程序崩潰退出等好處。
但是 defer 也有副作用,它會使資源延遲釋放,defer 儘量不要再 for-loop 中使用,並且相比於未使用 defer 調用的函數(自定義和部分內置)或方法,defer 也有一定的性能損耗,Golang 語言官方也在 golang 1.13 和 golang 1.14 中優化了 defer 的性能。
相比於 defer 的性能損耗,defer 帶來的使代碼更加優雅、可讀和健壯等優勢,我認爲 defer 綜合來看,利大於弊,它可以給 gopher 們帶來的收益比付出的代價更大。所以,我建議大家儘量使用 defer。
還有一點需要注意的是,我們不要使用 defer 調用有返回值的自定義函數或方法,返回值會丟失,可能會給應用程序帶來意想不到的錯誤。
04
總結
本文我們介紹了 defer 的執行機制,使用場景和注意事項,並且給出了相應的示例代碼。通常我們會在 Golang 語言開發中使用 defer 防止忘記釋放資源(關閉文件或解鎖)和捕獲 panic(同一 goroutine 中) 防止應用程序崩潰退出。
關於 defer 的原理分析,限於篇幅,並沒有花費筆墨,感興趣的讀者朋友們,建議自行搜索相關資料瞭解相關內容。
參考資料:
https://gobyexample-cn.github.io/defer
https://blog.golang.org/defer-panic-and-recover
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/_5Vvop0vKIHZwbxJjfX4AA