go defer詳解


一、defer 的作用和執行時機

go 的 defer 語句是用來延遲執行函數的,而且延遲發生在調用函數 return之后,比如

func a() int { defer b() return 0 } 

b 的執行是發生在return 0之后,注意defer的語法,關鍵字defer之后是函數的調用。

二、defer 的重要用途一:清理釋放資源

由於defer的延遲特性,defer常用在函數調用結束之后清理相關的資源,比如

f, _ := os.Open(filename) defer f.Close() 

文件資源的釋放會在函數調用結束之后借助defer自動執行,不需要時刻記住哪里的資源需要釋放,打開和釋放必須相對應。

用一個例子深刻詮釋一下defer帶來的便利和簡潔。

代碼的主要目的是打開一個文件,然后復制內容到另一個新的文件中,沒有defer時這樣寫:

func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { return } dst, err := os.Create(dstName) if err != nil { //1 return } written, err = io.Copy(dst, src) dst.Close() src.Close() return }

代碼在#1處返回之后,src文件沒有執行關閉操作,可能會導致資源不能正確釋放,改用defer實現:

func CopyFile(dstName, srcName string) (written int64, err error) { src, err := os.Open(srcName) if err != nil { return } defer src.Close() dst, err := os.Create(dstName) if err != nil { return } defer dst.Close() return io.Copy(dst, src) }

srcdst都能及時清理和釋放,無論return在什么地方執行。

鑒於defer的這種作用,defer常用來釋放數據庫連接,文件打開句柄等釋放資源的操作。

###defer的重要用途二:執行recover

defer的函數在return之后執行,這個時機點正好可以捕獲函數拋出的panic,因而defer的另一個重要用途就是執行recover

recover只有在defer中使用才更有意義,如果在其他地方使用,由於program已經調用結束而提前返回而無法有效捕捉錯誤。

package main import ( "fmt" ) func main() { defer func() { if ok := recover(); ok != nil { fmt.Println("recover") } }() panic("error") }

記住defer要放在panic執行之前。

三、多個 defer 的執行順序

defer 的作用就是把關鍵字之后的函數執行壓入一個棧中延遲執行,多個defer的執行順序是后進先出LIFO

defer func() { fmt.Println("1") }() defer func() { fmt.Println("2") }() defer func() { fmt.Println("3") }() 

輸出順序是 321。

這個特性可以對一個array實現逆序操作。

四、被 deferred 函數的參數在 defer 時確定

這是defer的特點,一個函數被defer時,它的參數在defer時進行計算確定,即使defer之后參數發生修改,對已經defer的函數沒有影響,什么意思?看例子:

func a() { i := 0 defer fmt.Println(i) i++ return }

a 執行輸出的是 0 而不是 1,因為defer時,i 的值是 0,此時被defer的函數參數已經進行執行計算並確定了。

再看一個例子:

func calc(index string, a, b int) int { ret := a + b fmt.Println(index, a, b, ret) return ret } func main() { a := 1 b := 2 defer calc("1", a, calc("10", a, b)) a = 0 return }

執行代碼輸出

10 1 2 3 1 1 3 4

defer函數的參數 第三個參數在defer時就已經計算完成並確定,第二個參數 a 也是如此,無論之后 a 變量是否修改都不影響。

五、被defer的函數可以讀取和修改帶名稱的返回值

func c() (i int) { defer func() { i++ }() return 1 }

defer的函數是在return之后執行,可以修改帶名稱的返回值,上面的函數 c 返回的是 2。

 

六、defer、return的執行順序

https://www.cnblogs.com/l199616j/p/15500631.html

 

 

 

參考:https://zhuanlan.zhihu.com/p/60005467

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM