Golang 語言中的 Defer 怎么使用?
01介紹
在 Golang 語言中,我們可以在函數(自定義和部分內置)或方法中使用 defer 關鍵字注冊延遲調用(一個或多個),多個延遲調用的執(zhí)行順序是先進后出(FILO)。并且不會受到函數執(zhí)行結束退出,顯式調用 return 和主動(或被動)觸發(fā) panic 的影響,注冊成功的所有延遲調用都會被執(zhí)行,除非 defer 注冊在 return 之后或者函數(或方法)調用 os.Exit(1)。
defer 注冊多個延遲調用,執(zhí)行順序是先進后出(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 所在的函數或方法中,如果調用 os.Exit(1),defer 即便注冊,也不會執(zhí)行。
示例代碼:
- 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 注冊的函數(自定義和部分內置)或方法,因為不會受到函數執(zhí)行結束,顯式調用 return 和主動(或被動)觸發(fā) 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 中優(yōu)化了 defer 的性能。
相比于 defer 的性能損耗,defer 帶來的使代碼更加優(yōu)雅、可讀和健壯等優(yōu)勢,我認為 defer 綜合來看,利大于弊,它可以給 gopher 們帶來的收益比付出的代價更大。所以,我建議大家盡量使用 defer。
還有一點需要注意的是,我們不要使用 defer 調用有返回值的自定義函數或方法,返回值會丟失,可能會給應用程序帶來意想不到的錯誤。
04總結
本文我們介紹了 defer 的執(zhí)行機制,使用場景和注意事項,并且給出了相應的示例代碼。通常我們會在 Golang 語言開發(fā)中使用 defer 防止忘記釋放資源(關閉文件或解鎖)和捕獲 panic(同一 goroutine 中) 防止應用程序崩潰退出。
本文轉載自微信公眾號「Golang語言開發(fā)棧」,可以通過以下二維碼關注。轉載本文請聯系Golang語言開發(fā)棧公眾號。