Golang 的defer執行規則

2021-08-21 06:33:28 字數 2754 閱讀 8994

defer是golang的乙個特色功能,被稱為「延遲呼叫函式」。當外部函式返回後執行defer。類似於其他語言的 try… catch … finally… 中的finally,當然差別還是明顯的。在使用defer之前我們應該多了解defer的特性,這樣才能避免使用上的誤區。

1. 最簡單的defer

func test()()

//todo

//...

return

//defer執行時機

}

我們可以稍微改動一下上述**再次確認defer的執行時機。

func main() 

func test() (i int) ()

defer

func() ()

//todo

//...

fmt.println(0)

return

1//defer執行時機

}

output:

0 1 2

從上面示例可以發現defer執行是在return之後,且按照defer宣告的先進後出順序執行。以下是真實場景中比較常見的用法。

釋放占用的資源

func test() error 

//放在判斷err狀態之後

defer file.close()

//todo

//...

return

nil//defer執行時機

}

捕捉處理異常

func test2() 

}()file, err := os.open("path")

if err != nil

defer file.close()

//todo

//...

return

//defer執行時機

}

輸出日誌 等收尾工作

func test3() ()

//todo

//...

return

//defer執行時機

}

2. 複雜的defer

當我們已經深深記住defer的執行時機並打算翻過這一頁時,事情的發展又開始偏離初始。請看如下**

func test() (i)    //輸出 0

defer

func(x *int) (&i) //輸出 1

defer

func() () //輸出 1

i++//todo

//...

fmt.println(i) //輸出 1

return

}

output:

1

1 1

0 // 值未修改

0 // 值未修改

通常認為defer就像真的被挪到了return之後。但defer的本質依然是函式呼叫。當執行到defer定義時,首先會對引數進行求值,然後引數被壓入函式呼叫棧,此時不會進入defer函式體,而是直到函式返回時才呼叫defer函式體。引數被壓入函式呼叫棧時,如果引數是值型別,那麼將複製值,如果引數是指標,那麼將複製指標而不是複製指標指向的值。defer函式體內的變數是在return後執行因此不受影響。因此我們在使用defer時一定要明確函式的引數型別(如果有的話),其次要明確defer函式體內的變數引用是否正確。

以下是常見錯誤

func test4() error 

defer

func() ()//錯誤: 關閉是b檔案,f引用被重新賦值

f, err = os.open("b.txt")

if err != nil

defer

func() () //關閉是b檔案

list := int

for _, i := range list () //錯誤: 輸出 2 2 //函式體內對i引用,留最終值

}return

nil}

3. 更複雜點的defer

且看如下**

type test struct 

func (this *test) point()

func (this test) value()

func test5() , , }

for _, t := range ts

}

看似相同的**卻輸出了完全不同的結果。要理解這種差別還是得從呼叫函式的本質來說。golang對struct 的方法呼叫是這樣的

defer func (this type, para) result

struct 的方法在定義時this 採用的值型別還是指標決定defer在呼叫時首個引數(隱藏)的複製的不同。上述**中point方法定義時使用了指標作為this,因此輸出是for迴圈最終賦值的t引用。value方法定義時使用值型別作為this,因此輸出是for迴圈執行的每步複製後的t。

最終只有理解了以上出現的問題,到此defer所帶來困擾才能遠離我們。

golang 中多個 defer 的執行順序

引用 ture go 中的乙個示例 package main import fmt func main fmt.println done 程式執行結果為 counting done98 7654 3210從結果可以看出,defer的執行可以看做是乙個filo first in last out 棧。...

golang關於defer執行順序的面試題

這段 的執行結果是什麼?func main func defer call defer func defer func panic 觸發異常 執行結果 列印後列印中 列印前panic 觸發異常 goroutine 1 running main.defer call d golandworkapace...

Golang中的defer使用

在golang當中,defer 塊會在函式呼叫鍊錶中增加乙個函式呼叫。這個函式呼叫不是普通的函式呼叫,而是會在函式正常返回,也就是return之後新增乙個函式呼叫。因此,defer通常用來釋放函式內部變數。為了更好的學習defer的行為,我們首先來看下面一段 func copyfile dstnam...