1:延遲函數傳遞的參數是值
func deferTest() { var a = 1 defer fmt.Println(a) a = 2 return }
結論:延遲函數 fmt.Println(a) 的參數在 defer 語句出現的時候就已經確定下來了,所以不管后面如何修改 a 變量,都不會影響延遲函數
2:延遲函數傳遞的參數是地址
func deferTest() { var arr = [3]int{1, 2, 3} defer printTest(&arr) arr[0] = 4 return } func printTest(array *[3]int) { for i := range array { fmt.Println(array[i]) } }
結論:延遲函數 printTest() 的參數在 defer 語句出現的時候就已經確定下來了,即為數組的地址,延遲函數執行的時機是在 return 語句之前,所以對數組的最終修改的值會被打印出來。
3:延遲函數可能會影響函數的返回值
fmt.Println(deferTest) func deferTest() (result int) { i := 1 defer func() { result = 2 }() return i }
結論:函數的 return 語句并不是原子級的,實際的執行過程為為設置返回值—>ret,defer 語句是在返回前執行,所以返回過程是:「設置返回值—>執行defer—>ret」。所以 return 語句先把 result 設置成 i 的值(1),defer 語句中又把 result設置為 2 ,所以最終返回值為 2
4:defer需要定義在panic前
func panicBeforeDefer() { panic("a") defer func() { fmt.Println("b") }() } func panicAfterDefer() { defer func() { fmt.Println("b") }() panic("a") }
結論:代碼執行到了painc之后再執行的defer,然后按照defer的先進后出的順序執行defer,最后才執行panic。那為什么panic時會執行defer,可以看下這段代碼就很清楚了。
func gopanic(e interface{}) { gp := getg() ... var p _panic p.arg = e p.link = gp._panic gp._panic = (*_panic)(noescape(unsafe.Pointer(&p))) for { d := gp._defer if d == nil { break } ... } }
5:先判斷err,再defer釋放資源
func openFile() { file, err := os.Open("txt") if err != nil { return } defer file.Close() }
結論:獲取文件資源的時候會返回err,如果我們在后續需要進行defer釋放文件資源時,這里需要對err進行判斷。因為如果獲取文件資源失敗的時候不需要進行釋放,也避免了沒獲取到資源可能導致的釋放函數執行錯誤。
6:os.Exit時defer不會被執行
func deferExit() { defer func() { fmt.Println("exit") }() os.Exit(1) }
結論:上面代碼中的defer不會得到執行,因為os.Exit()用于立即中止程序,不可能恢復或運行延遲清理語句,不像panic會去找goroutine的defer鏈表。
審核編輯:劉清
-
defer
+關注
關注
0文章
2瀏覽量
1124
原文標題:【Golang】defer的這些坑,你遇到過嗎?
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論