go 語言中延遲函式 defer 充當著 try...catch 的重任,使用起來也非常簡便,然而在實際應用中,很多 gopher 並沒有真正搞明白 defer、return、返回值、panic 之間的執行順序,從而掉進坑中,今天我們就來揭開它的神秘面紗!
先來執行下面兩段**:
package mainimport (
"fmt")
func main()
func a()
int()
defer func() ()
return
i}
package mainimport (
"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 mainimport (
"fmt")
func main()
func c() *int
() defer func() ()
return &i
}
雖然 c()int 的返回值沒有被提前宣告,但是由於 c()int 的返回值是指標變數,那麼在 return 將變數 i 的位址賦給返回值後,defer 再次修改了 i 在記憶體中的實際值,因此 return 呼叫 ret 退出函式時返回值雖然依舊是原來的指標位址,但是其指向的記憶體實際值已經被成功修改了。
即,我們假設的結論是正確的!
package mainimport (
"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 mainimport (
"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 mainimport (
"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語句會按照逆序執行,最後該函式返回。特別是當你在進行一些開啟資源的操作時,遇到錯誤需要提前返回,在返回前你需要關閉相應的資源,不然很容易造成資源洩露等問題。如下 所示,我們一般...