Go 延遲函式 defer 詳解

2022-09-17 14:06:13 字數 2407 閱讀 9424

go 語言中延遲函式 defer 充當著 try...catch 的重任,使用起來也非常簡便,然而在實際應用中,很多 gopher 並沒有真正搞明白 defer、return、返回值、panic 之間的執行順序,從而掉進坑中,今天我們就來揭開它的神秘面紗!

先來執行下面兩段**:

package main

import (

"fmt")

func main()

func a()

int()

defer func() ()

return

i}

package main

import (

"fmt")

func main()

func b() (i

int) ()

defer func() ()

return i //

或者直接 return 效果相同

}

多個 defer 的執行順序為「後進先出/先進後出」;

所有函式在執行 ret 返回指令之前,都會先檢查是否存在 defer 語句,若存在則先逆序呼叫 defer 語句進行收尾工作再退出返回;

匿名返回值是在 return 執行時被宣告,有名返回值則是在函式宣告的同時被宣告,因此在 defer 語句中只能訪問有名返回值,而不能直接訪問匿名返回值;

return 其實應該包含前後兩個步驟:第一步是給返回值賦值(若為有名返回值則直接賦值,若為匿名返回值則先宣告再賦值);第二步是呼叫 ret 返回指令並傳入返回值,而 ret 則會檢查 defer 是否存在,若存在就先逆序插播 defer 語句,最後 ret 攜帶返回值退出函式;

因此,‍‍defer、return、返回值三者的執行順序應該是:return最先給返回值賦值;接著 defer 開始執行一些收尾工作;最後 ret 指令攜帶返回值退出函式。

如何解釋兩種結果的不同:

上面兩段**的返回結果之所以不同,其實從上面的結論中已經很好理解了。

package main

import (

"fmt")

func main()

func c() *int

() defer func() ()

return &i

}

雖然 c()int 的返回值沒有被提前宣告,但是由於 c()int 的返回值是指標變數,那麼在 return 將變數 i 的位址賦給返回值後,defer 再次修改了 i 在記憶體中的實際值,因此 return 呼叫 ret 退出函式時返回值雖然依舊是原來的指標位址,但是其指向的記憶體實際值已經被成功修改了。

即,我們假設的結論是正確的!

package main

import (

"fmt""

time")

func main()

func p(t time.time)

//輸出結果:

//main 2017-08-01 14:59:47.547597041 +0800 cst

//defer 2017-08-01 14:59:42.545136374 +0800 cst

//p 2017-08-01 14:59:47.548833586 +0800 cst

defer 只對當前協程有效(main 可以看作是主協程);

當任意一條(主)協程發生 panic 時,會執行當前協程中 panic 之前已宣告的 defer;

在發生 panic 的(主)協程中,如果沒有乙個 defer 呼叫 recover()進行恢復,則會在執行完最後乙個已宣告的 defer 後,引發整個程序崩潰;

主動呼叫 os.exit(int) 退出程序時,defer 將不再被執行。

package main

import (

"errors""

fmt"

"time"//

"os"

)func main() ()

//會導致 defer 不會執行

//(2)panic(e)

//defer 會執行

time.sleep(1e9)

fmt.println(

"over.")

//(5)os.exit(1)

//defer 不會執行

}

defer 表示式會被放入乙個類似於棧( stack )的結構,所以呼叫的順序是先進後出/後進先出的。 

下面這段**輸出的結果是 4321 而不是 1234 。

package main

import (

"fmt")

func main()

GO語言延遲函式defer用法分析

defer 在宣告時不會立即執行,而是在函式 return 後,再按照 filo 先進後出 的原則依次執行每乙個 defer,一般用於異常處理 釋放資源 清理資料 記錄日誌等。這有點像物件導向語言的析構函式,優雅又簡潔,是 golang 的亮點之一。1 了解 defer 的執行順序 package ...

Go 學習筆記 延遲執行函式 defer

go語言中有種不錯的設計,即延遲 defer 語句,你可以在函式中新增多個defer語句。當函式執行到最後時,這些defer語句會按照逆序執行,最後該函式返回。特別是當你在進行一些開啟資源的操作時,遇到錯誤需要提前返回,在返回前你需要關閉相應的資源,不然很容易造成資源洩露等問題。如下 所示,我們一般...

Go 學習筆記 延遲執行函式 defer

go語言中有種不錯的設計,即延遲 defer 語句,你可以在函式中新增多個defer語句。當函式執行到最後時,這些defer語句會按照逆序執行,最後該函式返回。特別是當你在進行一些開啟資源的操作時,遇到錯誤需要提前返回,在返回前你需要關閉相應的資源,不然很容易造成資源洩露等問題。如下 所示,我們一般...