golang的defer是怎么工作的磷蛹?defer在golang里是一個(gè)很基礎(chǔ)的關(guān)鍵字,在函數(shù)內(nèi)部使用defer聲明的語句會(huì)在函數(shù)退出時(shí)執(zhí)行溪烤,那具體是怎么工作的那味咳?這里通過一個(gè)例子給大家講解下庇勃。
首先大家需要理解函數(shù)的執(zhí)行過程。我理解代碼在編譯的時(shí)候就已經(jīng)確定了函數(shù)的執(zhí)行過程槽驶,當(dāng)解析到defer關(guān)鍵字時(shí)责嚷,將defer函數(shù)放在defer堆棧的棧底,這樣如果一個(gè)函數(shù)有多個(gè)defer聲明時(shí)掂铐,就遵循先進(jìn)后出的原則罕拂,倒敘輸出,這也就是下面這個(gè)例子的輸出結(jié)果:
func defer_call() {?
????deferfunc() {
????????fmt.Println("打印前")
}()
defer func() {
????????fmt.Println("打印中")
}()?
defer func() {?
?????????fmt.Println("打印后")
}()
panic("觸發(fā)異常")
}
輸出結(jié)果:
打印后
打印中
打印前
上面的例子相對(duì)比較好理解全陨,和我們的堆棧以一摸一樣爆班。下面再和大家一起解析一個(gè)經(jīng)典的例子,如下:
func calc(index string,a,b int) int {?
????ret := a+b
? ?fmt.Println(index,a,b,ret)
????return ret
}
func main() {
????a:=1
????b:=2
????defer calc("1",a,calc("10",a,b))
?????a=0
?????defer calc("2",a,calc("20",a,b))
????b=1
}
大家可以有興趣的跑一下這個(gè)demo辱姨∈疗校看看輸出結(jié)果。輸出如下:
10 1 2 3
20 0 2 2
2 0 2 2?
1 1 3 4
下面帶領(lǐng)大家一起解析下炮叶。
第一步 解析上面的main函數(shù)
a =1 b =2 碗旅,然后遇到defer關(guān)鍵字,將defer聲明的calc壓入棧底镜悉,此時(shí)獲取變量的值分別為calc(1,1,calc("10",a,b)),發(fā)現(xiàn)第三個(gè)變量是一個(gè)函數(shù)祟辟,因此繼續(xù)將該函數(shù)解析運(yùn)算。
即第一次輸出calc("10",1,2)==>> 10,1,2,3, 結(jié)果ret=3.將上一步的calc(1,1,calc("10",a,b))替換為calc(1,1,3)侣肄,然后壓入到棧底旧困。
第二步 程序繼續(xù)解析,a=0稼锅,然后遇到第二個(gè)defer關(guān)鍵字吼具,解析defer后的函數(shù)calc("2",a,calc("20",a,b)),和第一步的情況一樣矩距,先進(jìn)行數(shù)值替換上式替換為calc("2",0,calc("20",0,b))拗盒,然后再將第三個(gè)參數(shù)函數(shù)calc("20",0,b)展開為calc("20",0,2),==>>20锥债,0陡蝇,2,2哮肚,結(jié)果ret=2.將上一步的calc("2",a,calc("20",a,b))替換為calc("2",0,2),然后壓入到棧底登夫。
經(jīng)過上面兩步驟,已經(jīng)輸出了10允趟,1恼策,2,3和20潮剪,0涣楷,2分唾,2。第一個(gè)例子里已經(jīng)說了 defer是按照先入后出的順序執(zhí)行总棵,那程序先執(zhí)行calc("2",0,2)==》2鳍寂,0,2情龄,2.然后再執(zhí)行calc(1,1,3)==>1,1,3,4.
支持整個(gè)程序輸出完成。
下面繼續(xù)說下defer與return的關(guān)系捍壤,通過下面一個(gè)經(jīng)典的例子骤视,大家請(qǐng)看下:
func DeferFunc1(i int) (t int) {
????t = i
????defer func() {
????????t +=3
????}()
????return t
}
func DeferFunc2(i int) int {
????t := i
? ? defer func() {
????????t +=3
????}()
????return t
}
func DeferFunc3(i int) (t int) {
? defer func() {
????????t += i
????}()
????return 2
}
package main?
func main() {
????println(DeferFunc1(1))
????println(DeferFunc2(1))
????println(DeferFunc3(1))
}
上面這個(gè)例子輸出信息是什么那?如果大家能夠打出來4 1 3就可以跳過下面的信息了鹃觉,接下來給大家詳解下专酗。
先說下defer與return的執(zhí)行順序,defer的執(zhí)行順序是在return之后盗扇,也就是說return操作執(zhí)行完之后祷肯,再執(zhí)行defer函數(shù),然后函數(shù)執(zhí)行結(jié)束疗隶。
先說下defer與return的執(zhí)行順序佑笋,defer的執(zhí)行順序是在return之后,也就是說return操作執(zhí)行完之后斑鼻,再執(zhí)行defer函數(shù)蒋纬,然后函數(shù)執(zhí)行結(jié)束。
先說下defer與return的執(zhí)行順序坚弱,defer的執(zhí)行順序是在return之后蜀备,也就是說return操作執(zhí)行完之后,再執(zhí)行defer函數(shù)荒叶,然后函數(shù)執(zhí)行結(jié)束碾阁。
重要的事情說3遍,本著上面的原則些楣,下面的
解析DeferFunc1
看下函數(shù)返回值脂凶,聲明了t變量,說明t變量的作用域?yàn)檎麄€(gè)函數(shù)體
1戈毒、將t賦值i艰猬,也就是1
2、執(zhí)行return t埋市,此時(shí)t=t=1
3冠桃、執(zhí)行defer,t+=3道宅,這樣t=4
4食听、然后函數(shù)結(jié)束胸蛛,經(jīng)過第三步的操作 t已經(jīng)變成了4,所以函數(shù)結(jié)束后樱报,t變量為4
解析DeferFunc2
變量t的作用域?yàn)楹瘮?shù)內(nèi)
1葬项、將t賦值i,也就是1
2迹蛤、執(zhí)行return t民珍,此時(shí)t=1
3、執(zhí)行defer盗飒,t+=3嚷量,這樣t=4
4、然后函數(shù)結(jié)束逆趣,因?yàn)樽兞縯的作用域?yàn)楹瘮?shù)內(nèi)蝶溶,所以函數(shù)結(jié)束后,經(jīng)過第2步宣渗,將return值賦為1抖所,第三步的4并不會(huì)返回。說到這里可能比較難懂痕囱,下面將DeferFunc2修改一下田轧,大家就會(huì)恍然大悟
func DeferFunc2(i int) (result int) {
????t := i
defer func() {
????????t +=3
????}()
return t
}
之前返回值的name給隱藏了,我顯示的定義一個(gè)result咐蝇。然后我們?cè)僖徊讲降慕馕鱿?/p>
1涯鲁、將t賦值i,也就是1
2有序、執(zhí)行return t抹腿,此時(shí)result = t => result=1
3、執(zhí)行defer旭寿,t+=3警绩,這樣t=4
大家再看看函數(shù)執(zhí)行完之后,返回值是多少盅称,這樣大家很容易就知道應(yīng)該是1了吧肩祥?還不懂的同學(xué)從頭再讀一遍吧 ??
解析DeferFunc3
1、執(zhí)行return 2缩膝,return t = 2
2混狠、執(zhí)行defer,t+=1=》t = 3
函數(shù)執(zhí)行結(jié)束疾层,t=3将饺。