Go語(yǔ)言中defer使用的陷阱小結(jié)
01 介紹
什么是 defer
defer 是Go語(yǔ)言提供的一種用于注冊(cè)延遲調(diào)用的機(jī)制,以用來(lái)保證一些資源被回收和釋放。
defer 注冊(cè)的延遲調(diào)用可以在當(dāng)前函數(shù)執(zhí)行完畢后執(zhí)行(包括通過(guò)return正常結(jié)束或者panic導(dǎo)致的異常結(jié)束)
當(dāng)defe注冊(cè)了的函數(shù)或表達(dá)式逆序執(zhí)行,先注冊(cè)的后執(zhí)行,類(lèi)似于棧 ”先進(jìn)后出“
下面看一個(gè)例子:
package main import "fmt" func main() { f() } func f() { defer func() { fmt.Println(1) }() defer func() { fmt.Println(2) }() defer func() { fmt.Println(3) }() }
輸出:
3
2
1
如何使用defer
釋放資源
使用 defer 可以在一定程度上避免資源泄漏,尤其是有很多 return 語(yǔ)句的場(chǎng)景,很容易忘記或者由于邏輯上的錯(cuò)誤導(dǎo)致資源沒(méi)有關(guān)閉。
下面的程序便是因?yàn)槭褂?return 后,關(guān)閉資源的語(yǔ)句沒(méi)有執(zhí)行,導(dǎo)致資源泄漏:
f, err := os.Open("test.txt") if err != nil { return } f.process() f.Close()
此處更好的做法如下:
f, err := os.Open("test.txt") if err != nil { return } defer f.Close() // 對(duì)文件進(jìn)行操作 f,process()
此處當(dāng)程序順利執(zhí)行后,defer 會(huì)釋放資源;defer 需要先注冊(cè)后使用,比如此處,打開(kāi)文件異常時(shí),程序執(zhí)行到 return 語(yǔ)句時(shí)便會(huì)退出當(dāng)前函數(shù),沒(méi)有經(jīng)過(guò) defer,所以此處defer 不會(huì)執(zhí)行
defer 捕獲異常
在 go 中沒(méi)有 try 和 catch , 當(dāng)程序出現(xiàn)異常是,我們需要從異常中恢復(fù)。我們這時(shí)可以利用 defer + recover 進(jìn)行異常捕獲
func f() { defer func() { if err := recover(); err != nil { fmt.Println(err) } }() // do something panic("panic") }
注意,recover() 函數(shù)在在defer中用匿名函數(shù)調(diào)用才有效,以下程序不能進(jìn)行異常捕獲:
func f() { if err := recover(); err != nil { fmt.Println(err) } // do something panic("panic") }
實(shí)現(xiàn)代碼追蹤
下面提供一個(gè)方法能追蹤到程序時(shí)進(jìn)入或離開(kāi)某個(gè)函數(shù)的信息,此處可以用來(lái)測(cè)試特定函數(shù)有沒(méi)有被執(zhí)行
func trace(msg string) { fmt.Println("entering:", msg) } func untrace(msg string) { fmt.Println("leaving:", msg) }
記錄函數(shù)的參數(shù)與返回值
有時(shí)候程序返回結(jié)果不符合預(yù)期是, 大家可能手動(dòng)打印 log 調(diào)試,此時(shí)使用 defer 記錄函數(shù)的參數(shù)和返回值,避免手動(dòng)多處打印調(diào)試語(yǔ)句
func func1(s string) (n int, err error) { defer func() { log.Printf("func1(%q) = %d, %v", s, n, err) }() return 7, nil }
實(shí)現(xiàn)代碼追蹤 和 記錄函數(shù)的參數(shù)與返回值
在 Go 語(yǔ)言中,defer 一般用于資源釋放,或使用 defer 調(diào)用一個(gè)匿名函數(shù),在匿名函數(shù)中使用 recover() 處理異常 panic。
在使用 defer 時(shí),也很容易遇到陷阱,本文我們介紹使用 defer 時(shí)有哪些陷阱。
02 defer 陷阱
defer 語(yǔ)句不可以在 return 語(yǔ)句之后。
示例代碼:
func main() { name := GetUserName("phper") fmt.Printf("name:%s\n", name) if name != "gopher" { return } defer fmt.Println("this is a defer call") } func GetUserName(name string) string { return name }
輸出結(jié)果:
name:phper
閱讀上面這段代碼,我們?cè)?return 語(yǔ)句之后執(zhí)行 defer 語(yǔ)句,通過(guò)輸出結(jié)果可以發(fā)現(xiàn) defer 語(yǔ)句調(diào)用未執(zhí)行。
雖然 defer 可以在函數(shù)體中的任意位置,我們也是需要特別注意使用 defer 的位置是否可以執(zhí)行。
defer 語(yǔ)句執(zhí)行匿名函數(shù),參數(shù)預(yù)處理。
示例代碼:
func main() { var count int64 defer func(data int64) { fmt.Println("defer:", data) }(count + 1) count = 100 fmt.Println("main:", count) }
輸出結(jié)果:
main: 100
defer: 1
閱讀上面這段代碼,首先我們定義一個(gè)類(lèi)型為 int64 的變量 count,然后使用 defer 語(yǔ)句執(zhí)行一個(gè)匿名函數(shù),匿名函數(shù)傳遞參數(shù)為 count + 1,最終 main 函數(shù)輸出 100,defer 執(zhí)行的匿名函數(shù)輸出 1。
因?yàn)樵趫?zhí)行 defer 語(yǔ)句時(shí),執(zhí)行了 count + 1,并先將其存儲(chǔ),等到 defer 所在的函數(shù)體 main 執(zhí)行完,再執(zhí)行 defer 語(yǔ)句調(diào)用的匿名函數(shù)的函數(shù)體中的代碼。
03 總結(jié)
本文主要介紹在使用 defer 語(yǔ)句時(shí)可能會(huì)遇到的陷阱。分別是 defer 語(yǔ)句不可以在 return 語(yǔ)句之后;defer 語(yǔ)句執(zhí)行的匿名函數(shù),匿名函數(shù)的參數(shù)會(huì)被預(yù)先處理。
到此這篇關(guān)于Go語(yǔ)言中defer使用的陷阱小結(jié)的文章就介紹到這了,更多相關(guān)Go語(yǔ)言 defer使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語(yǔ)言中的復(fù)合類(lèi)型詳細(xì)介紹
這篇文章主要介紹了Go語(yǔ)言中的復(fù)合類(lèi)型詳細(xì)介紹,復(fù)合類(lèi)型包括:結(jié)構(gòu)體、數(shù)組、切片、Maps,需要的朋友可以參考下2014-10-10golang 設(shè)置web請(qǐng)求狀態(tài)碼操作
這篇文章主要介紹了golang 設(shè)置web請(qǐng)求狀態(tài)碼操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12Golang基礎(chǔ)教程之字符串string實(shí)例詳解
這篇文章主要給大家介紹了關(guān)于Golang基礎(chǔ)教程之字符串string的相關(guān)資料,需要的朋友可以參考下2022-07-07如何使用Go語(yǔ)言實(shí)現(xiàn)遠(yuǎn)程執(zhí)行命令
遠(yuǎn)程執(zhí)行命令最常用的方法就是利用SSH協(xié)議,將命令發(fā)送到遠(yuǎn)程機(jī)器上執(zhí)行,并獲取返回結(jié)果。本文將介紹如何使用Go語(yǔ)言實(shí)現(xiàn)遠(yuǎn)程執(zhí)行命令。下面一起來(lái)看看。2016-08-08