defer在go中的執(zhí)行順序是后進(jìn)先出(lifo),1.遇到defer語句時(shí),函數(shù)調(diào)用會(huì)被壓入棧中,2.當(dāng)前函數(shù)返回前,棧頂?shù)膁efer函數(shù)依次彈出執(zhí)行;例如連續(xù)兩個(gè)defer,后寫的先執(zhí)行。defer參數(shù)在注冊時(shí)求值,如i++不影響已保存的值。return前會(huì)執(zhí)行defer,可能影響命名返回值的結(jié)果。注意事項(xiàng)包括:避免循環(huán)中頻繁使用defer、不在defer中做耗時(shí)操作、注意作用域及副作用。
在 golang 中,defer 是一個(gè)非常實(shí)用的關(guān)鍵字,用來延遲執(zhí)行某個(gè)函數(shù)調(diào)用,通常用于資源釋放、鎖的釋放等場景。但很多人剛接觸時(shí)會(huì)對其執(zhí)行順序感到困惑,尤其是當(dāng)多個(gè) defer 出現(xiàn)的時(shí)候。
其實(shí),只要理解 defer 的底層機(jī)制是基于棧結(jié)構(gòu)實(shí)現(xiàn)的,它的執(zhí)行順序就很容易理解了。
defer 的執(zhí)行順序是后進(jìn)先出(LIFO)
Golang 在遇到 defer 語句時(shí),并不會(huì)立即執(zhí)行對應(yīng)的函數(shù),而是將其壓入一個(gè)函數(shù)調(diào)用棧中。等到當(dāng)前函數(shù)即將返回之前,才會(huì)從棧頂開始依次彈出并執(zhí)行這些被推遲的函數(shù)調(diào)用。
立即學(xué)習(xí)“go語言免費(fèi)學(xué)習(xí)筆記(深入)”;
舉個(gè)簡單的例子:
func main() { defer fmt.Println("first") defer fmt.Println("second") fmt.Println("hello world") }
輸出結(jié)果是:
hello world second first
雖然兩個(gè) defer 是按順序?qū)懙模捎谒鼈儽粔喝霔V校瑘?zhí)行順序就是反過來的。這和我們?nèi)粘@斫獾摹白詈髮懙南葓?zhí)行”是一致的。
defer 與函數(shù)參數(shù)求值時(shí)機(jī)
一個(gè)容易忽略的細(xì)節(jié)是:defer 后面的函數(shù)參數(shù)是在 defer 被執(zhí)行時(shí)就完成求值的,而不是等到真正執(zhí)行該函數(shù)時(shí)才計(jì)算。
看個(gè)例子:
func demo() { i := 0 defer fmt.Println(i) i++ }
這段代碼輸出的是 0,不是 1。因?yàn)?i 的值在 defer 執(zhí)行時(shí)就已經(jīng)確定為 0 了,后續(xù)的 i++ 并不會(huì)影響已經(jīng)保存下來的值。
所以記住一點(diǎn):defer 注冊時(shí)就完成了參數(shù)求值。
defer 與 return 的關(guān)系
很多人以為 return 之后就不能再執(zhí)行 defer 了,實(shí)際上并不是這樣。Go 在函數(shù)返回前,會(huì)先處理所有注冊好的 defer 調(diào)用,然后再真正退出函數(shù)。
例如:
func f() int { var i int defer func() { i++ }() return i }
這個(gè)函數(shù)返回的其實(shí)是 1,而不是 0。因?yàn)?return i 的值已經(jīng)被準(zhǔn)備好了,但在返回前執(zhí)行了 defer,修改了 i 的值。
這種情況說明:defer 可以影響帶命名返回值的函數(shù)的結(jié)果。
使用 defer 的一些注意事項(xiàng)
- 不要在循環(huán)中頻繁使用 defer,可能會(huì)導(dǎo)致性能問題。
- 避免在 defer 中做耗時(shí)操作,因?yàn)樗鼤?huì)影響函數(shù)的返回速度。
- defer 的作用域是函數(shù)級別的,不能跨函數(shù)傳遞。
- 如果 defer 函數(shù)有副作用(比如修改變量),要特別小心它的執(zhí)行順序。
總的來說,理解 defer 的執(zhí)行順序并不難,關(guān)鍵在于它背后的“棧結(jié)構(gòu)”機(jī)制。掌握了這一點(diǎn),很多看似奇怪的行為其實(shí)都合情合理。
基本上就這些,不復(fù)雜但容易忽略。