Golang 語言中的 Defer 怎么使用?
01介紹
在 Golang 語言中,我們可以在函數(shù)(自定義和部分內(nèi)置)或方法中使用 defer 關(guān)鍵字注冊延遲調(diào)用(一個或多個),多個延遲調(diào)用的執(zhí)行順序是先進(jìn)后出(FILO)。并且不會受到函數(shù)執(zhí)行結(jié)束退出,顯式調(diào)用 return 和主動(或被動)觸發(fā) panic 的影響,注冊成功的所有延遲調(diào)用都會被執(zhí)行,除非 defer 注冊在 return 之后或者函數(shù)(或方法)調(diào)用 os.Exit(1)。
defer 注冊多個延遲調(diào)用,執(zhí)行順序是先進(jìn)后出(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 沒有注冊,將不會執(zhí)行。
示例代碼:
- func main () {
- fmt.Println("main")
- return
- defer func() {
- fmt.Println("A")
- }()
- }
defer 所在的函數(shù)或方法中,如果調(diào)用 os.Exit(1),defer 即便注冊,也不會執(zhí)行。
示例代碼:
- func main () {
- defer func() {
- fmt.Println("A")
- }()
- fmt.Println("main")
- os.Exit(1)
- }
defer 必須在函數(shù)和方法中才可以使用,并且 defer 后面必須是函數(shù)(自定義和部分內(nèi)置函數(shù))或方法,defer 函數(shù)的實(shí)參是值拷貝。
示例代碼
- func main () {
- a := 0
- defer func(num int) {
- fmt.Println("defer func()", num)
- }(a)
- a++
- fmt.Println(a)
- }
02使用場景
使用關(guān)鍵字 defer 注冊的函數(shù)(自定義和部分內(nèi)置)或方法,因?yàn)椴粫艿胶瘮?shù)執(zhí)行結(jié)束,顯式調(diào)用 return 和主動(或被動)觸發(fā) panic 的影響,通常會用于防止忘記釋放資源和捕獲 panic(同一 goroutine 中) 防止應(yīng)用程序崩潰退出的應(yīng)用場景。
示例代碼:
- 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 延遲調(diào)用釋放資源,防止忘記釋放資源(關(guān)閉文件或解鎖),通常 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 函數(shù),用于攔截 panic(同一 goroutine 中),防止程序崩潰退出。
03注意事項(xiàng)
雖然使用 defer 具有可以用于防止忘記釋放資源和攔截 panic(同一 goroutine 中)防止應(yīng)用程序崩潰退出等好處。
但是 defer 也有副作用,它會使資源延遲釋放,defer 盡量不要再 for-loop 中使用,并且相比于未使用 defer 調(diào)用的函數(shù)(自定義和部分內(nèi)置)或方法,defer 也有一定的性能損耗,Golang 語言官方也在 golang 1.13 和 golang 1.14 中優(yōu)化了 defer 的性能。
相比于 defer 的性能損耗,defer 帶來的使代碼更加優(yōu)雅、可讀和健壯等優(yōu)勢,我認(rèn)為 defer 綜合來看,利大于弊,它可以給 gopher 們帶來的收益比付出的代價更大。所以,我建議大家盡量使用 defer。
還有一點(diǎn)需要注意的是,我們不要使用 defer 調(diào)用有返回值的自定義函數(shù)或方法,返回值會丟失,可能會給應(yīng)用程序帶來意想不到的錯誤。
04總結(jié)
本文我們介紹了 defer 的執(zhí)行機(jī)制,使用場景和注意事項(xiàng),并且給出了相應(yīng)的示例代碼。通常我們會在 Golang 語言開發(fā)中使用 defer 防止忘記釋放資源(關(guān)閉文件或解鎖)和捕獲 panic(同一 goroutine 中) 防止應(yīng)用程序崩潰退出。
本文轉(zhuǎn)載自微信公眾號「Golang語言開發(fā)?!?,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請聯(lián)系Golang語言開發(fā)棧公眾號。