簡(jiǎn)單聊聊Golang中defer預(yù)計(jì)算參數(shù)
什么是defer
defer用來(lái)聲明一個(gè)延遲函數(shù),把這個(gè)函數(shù)放入到一個(gè)棧上, 當(dāng)外部的包含方法return之前,返回參數(shù)到調(diào)用方法之前調(diào)用,也可以說(shuō)是運(yùn)行到最外層方法體的"}"時(shí)調(diào)用。我們經(jīng)常用他來(lái)做一些資源的釋放,比如關(guān)閉io操作
func doSomething(fileName string) { file,err := os.Open(fileName) if err != nil { panic(err) } defer file.Close() }
defer 可以保證方法可以在外圍函數(shù)返回之前調(diào)用。有點(diǎn)像其他言的 try finally
try{ }finally{ }
Go語(yǔ)言defer預(yù)計(jì)算參數(shù)
Go 語(yǔ)言中所有的函數(shù)調(diào)用都是傳值的,雖然 defer 是關(guān)鍵字,但是也繼承了這個(gè)特性。假設(shè)我們想要計(jì)算 main 函數(shù)運(yùn)行的時(shí)間,可能會(huì)寫(xiě)出以下的代碼:
package main import ( "fmt" "time" ) func main() { startedAt := time.Now() defer fmt.Println(time.Since(startedAt)) time.Sleep(time.Second) //休眠一秒 }
結(jié)果是:
D:\workspace\go\src\test>go run main.go
0s
運(yùn)行結(jié)果并不符合我們的預(yù)期,這個(gè)現(xiàn)象背后的原因是什么呢?經(jīng)過(guò)分析,我們會(huì)發(fā)現(xiàn)調(diào)用 defer 關(guān)鍵字會(huì)立刻拷貝函數(shù)中引用的外部參數(shù),所以 time.Since(startedAt) 的結(jié)果不是在 main 函數(shù)退出之前計(jì)算的,而是在 defer 關(guān)鍵字調(diào)用時(shí)計(jì)算的【defer入棧的時(shí)候】,最終導(dǎo)致上述代碼輸出 0s
我們?cè)賮?lái)看個(gè)簡(jiǎn)單例子來(lái)說(shuō)明上述解釋?zhuān)?/p>
package main import ( "fmt" ) func main() { i := 1 defer fmt.Println(test(i)) i = 100 } func test(i int) int { i = i + 1 return i } D:\workspace\go\src\test>go run main.go 2
當(dāng)代碼運(yùn)行到defer fmt.Println(test(i))的時(shí)候,會(huì)把defer右邊最外層函數(shù)的參數(shù)計(jì)算完畢,并傳遞進(jìn)函數(shù)里,但不會(huì)執(zhí)行函數(shù)體的代碼直到包裹defer的函數(shù)返回。我們先看會(huì)把defer右邊最外層函數(shù)的參數(shù)計(jì)算完畢,并傳遞進(jìn)函數(shù)里這句話,對(duì)應(yīng)例子就是先把test(i)算出來(lái),此時(shí)i=1,計(jì)算test(1)得2,然后fmt.Println(2)入棧,等到最后程序運(yùn)行完了再運(yùn)行defer結(jié)果就是2(但不會(huì)執(zhí)行函數(shù)體的代碼直到包裹defer的函數(shù)返回)。
我們?cè)賮?lái)看一個(gè)例子與匿名函數(shù)結(jié)合:
package main import ( "fmt" ) func main() { i := 1 defer func() { fmt.Println(test(i)) }() i = 100 } func test(i int) int { i = i + 1 return i }
結(jié)果:
D:\workspace\go\src\test>go run main.go
101
使用匿名函數(shù),結(jié)果是101,相當(dāng)于i給到test方法的是100,那為什么呢?還是那句話:但不會(huì)執(zhí)行函數(shù)體的代碼直到包裹defer的函數(shù)返回
也就是說(shuō)他會(huì)把整個(gè){ fmt.Println(test(i)) }()函數(shù)體入棧,等到最后程序運(yùn)行完了再運(yùn)行defer,此時(shí)的i是100,運(yùn)行test后就是101了。
所以你要解決第一個(gè)打印為0s的問(wèn)題,你就可以使用匿名函數(shù)來(lái)解決,如下:
package main import ( "fmt" "time" ) func main() { startedAt := time.Now() defer func() { fmt.Println(time.Since(startedAt)) }() time.Sleep(time.Second) //休眠一秒 }
結(jié)果:
D:\workspace\go\src\test>go run main.go
1.0152825s
總結(jié)
到此這篇關(guān)于Golang中defer預(yù)計(jì)算參數(shù)的文章就介紹到這了,更多相關(guān)Go defer預(yù)計(jì)算參數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang通過(guò)node_exporter監(jiān)控GPU及cpu頻率、溫度的代碼
node_exporter這個(gè)開(kāi)源組件是配合prometheus收集主機(jī)操作系統(tǒng)層的metrics的常用組件,但是官方?jīng)]有提供GPU卡的metrics的采集,今天通過(guò)本文給大家介紹golang通過(guò)node_exporter監(jiān)控GPU及cpu頻率、溫度的相關(guān)知識(shí),感興趣的朋友一起看看吧2022-05-05Golang中Bit數(shù)組的實(shí)現(xiàn)方式
這篇文章主要介紹了Golang中Bit數(shù)組的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04Golang初始化MySQL數(shù)據(jù)庫(kù)方法淺析
這篇文章主要介紹了Golang初始化MySQL數(shù)據(jù)庫(kù)的方法,數(shù)據(jù)庫(kù)的建立第一步即要初始化,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-05-05Go語(yǔ)言fmt.Sprintf格式化輸出的語(yǔ)法與實(shí)例
Go 可以使用 fmt.Sprintf 來(lái)格式化字符串,下面這篇文章主要給大家介紹了關(guān)于Go語(yǔ)言fmt.Sprintf格式化輸出的語(yǔ)法與實(shí)例,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-07-07Go并發(fā)控制Channel使用場(chǎng)景分析
使用channel來(lái)控制子協(xié)程的優(yōu)點(diǎn)是實(shí)現(xiàn)簡(jiǎn)單,缺點(diǎn)是當(dāng)需要大量創(chuàng)建協(xié)程時(shí)就需要有相同數(shù)量的channel,而且對(duì)于子協(xié)程繼續(xù)派生出來(lái)的協(xié)程不方便控制2021-07-07Go語(yǔ)言實(shí)現(xiàn)配置熱加載的方法分享
web項(xiàng)目,經(jīng)常需要熱啟動(dòng)各種各樣的配置信息,一旦這些服務(wù)發(fā)生變更,我們需要重新啟動(dòng)web server,以使配置生效,實(shí)現(xiàn)配置熱加載,本文為大家整理了幾個(gè)方法實(shí)現(xiàn)這個(gè)需求,需要的可以參考下2023-05-05