golang panic、recover函數(shù)以及defer語句

panic(運(yùn)行時恐慌)

demo:

fmt.Println("Enter function caller2.")
s1 := []int{0, 1, 2, 3, 4}
e5 := s1[5]
1 panic: runtime error: index of range
2
3 goroutine 1 [running]:
4 main.main()
5 /User/zheng/Golang_Puzzlers/demo.go:5 +0x3d
exit statu 2

這里的第一行是“panic: runtime error: index out of range”橄教。其中的“runtime error”的含義是,這是一個runtime代碼包中拋出的panic。在這個panic中证舟,包含了一個runtime.Error接口類型的值硕旗。runtime.Error接口內(nèi)嵌了error接口并做了一點(diǎn)點(diǎn)擴(kuò)展,runtime包中有不少它的實(shí)現(xiàn)類型女责。

實(shí)際上漆枚,此詳情中的“panic: ”右邊的內(nèi)容,正是這個panic包含的runtime.Error類型值的字符串表示形式抵知。

此外墙基,panic詳情中一般還會包含與它的引發(fā)原因有關(guān)的goroutine的代碼執(zhí)行信息。正如前述詳情中的“goroutine 1 [running]”,它表示有一個ID為1的goroutine在此panic被引發(fā)的時候正在運(yùn)行刷喜。

注意残制,這里的ID其實(shí)并不重要,因?yàn)樗皇荊o語言運(yùn)行時系統(tǒng)內(nèi)部給予的一個goroutine編號掖疮,我們在程序中是無法獲取和更改的初茶。

我們再看下一行, “main.main()”表明了這個goroutine包裝的go函數(shù)就是命令源碼文件中的那個main函數(shù)浊闪,也就是說這里的goroutine正是主goroutine恼布。再下面的一行,指出的就是這個goroutine中的哪一行
代碼在此panic被引發(fā)時正在執(zhí)行搁宾。

這包含了此行代碼在其所屬的源碼文件中的行數(shù)折汞,以及這個源碼文件的絕對路徑。這一行最后的+0x3d代表的是:此行代碼相對于其所屬函數(shù)的入口程序計數(shù)偏移量盖腿。不過爽待,一般情況下它的用戶并不大。

最后奸忽, “exit status 2”表明我的這個程序是以退出狀態(tài)碼2結(jié)束運(yùn)行的堕伪。在大多數(shù)操作系統(tǒng)中,只要退出狀態(tài)碼不是0栗菜,都意味著程序運(yùn)行的非正常結(jié)束欠雌。在Go語言中,因panic導(dǎo)致程序結(jié)束運(yùn)行的退出狀態(tài)碼一般都會是2疙筹。

從panic被引發(fā)到程序終止運(yùn)行的大致過程是什么富俄?

?某個函數(shù)中的某行代碼有意或無意地引發(fā)了一個panic。這時而咆,初始的panic詳情會被建立起來霍比,并且該程序的控制權(quán)會立即從此行代碼轉(zhuǎn)移至調(diào)用其所屬函數(shù)的那行代碼上,也就是調(diào)用棧中的上一級暴备。

?這也意味著悠瞬,此行代碼所屬函數(shù)的執(zhí)行隨即終止。緊接著,控制權(quán)并不會在此有片刻停留浅妆,它又會立即轉(zhuǎn)移至再上一級的調(diào)用代碼處望迎。控制權(quán)如此一級一級地沿著調(diào)用棧的反方向傳播至頂端凌外,也就是我們編寫的最外層函數(shù)那里辩尊。

?這里的外層函數(shù)指的就是go函數(shù),對于主goroutine來說就是main函數(shù)康辑。但是控制權(quán)也不會停留在那里摄欲,而是被Go語言運(yùn)行時系統(tǒng)收回。

?隨后疮薇,程序崩潰并終止運(yùn)行胸墙,承載程序這次運(yùn)行的進(jìn)程也會隨之死亡并消失。與此同時惦辛,在這個控制權(quán)傳播的過程中劳秋,panic詳情會被逐漸地積累和完善,并會在程序終止之前被打印出來胖齐。

怎樣施加應(yīng)對panic的保護(hù)措施玻淑,從而避免程序崩潰?

?Go語言的內(nèi)建函數(shù)recover專用于恢復(fù)panic,或者說平息運(yùn)行時恐慌呀伙。recover函數(shù)無需任何參數(shù)补履,并且會返回一個空接口類型的值。

?如果用法正確剿另,這個值實(shí)際上就是即將恢復(fù)的panic包含的值箫锤。并且,如果這個panic是因我們調(diào)用panic函數(shù)而引發(fā)的雨女,那么該值同時也會是我們此次調(diào)用panic函數(shù)時谚攒,傳入的參數(shù)值副本。

package main

import (
  "fmt"
  "errors"
)

func main() {
    fmt.Println("Enter function main.")
    // 引發(fā)panic
    panic(errors.New("something wrong"))
    p := recover()
    fmt.Printf("panic: %s\n", p)
    fmt.Println("Exit function main.")
}

&emsp:在上面的函數(shù)值氛堕,先通過panic函數(shù)引發(fā)了一個panic馏臭,緊接著通過recover函數(shù)恢復(fù)這個panic。結(jié)果:程序依然會崩潰讼稚,因?yàn)閜anic一旦發(fā)生括儒,控制權(quán)就會迅速地沿著調(diào)用棧的反方向傳播。所以锐想,在panic函數(shù)調(diào)用之后的代碼帮寻,根本沒有執(zhí)行的機(jī)會。應(yīng)該使用defer語句赠摇,defer語句就是用來延遲執(zhí)行代碼的固逗,延遲到該語句所在的函數(shù)即將執(zhí)行結(jié)束的那一刻浅蚪。

package main

import (
  "fmt"
  "errors"
)

func main() {
    fmt.Println("Enter function main.")
    defer func(){
        fmt.Println("Enter defer function.")
        if p := recover(); p != nil {
          fmt.Println("panic: %s\n",p)
        }
    }
    // 引發(fā)panic
    panic(errors.New("something wrong"))
    fmt.Println("Exit function main.")
}

如果一個函數(shù)中有多條defer語句,那么幾個defer函數(shù)調(diào)用的執(zhí)行順序是怎樣的抒蚜?

答: 在同一個函數(shù)中掘鄙,defer函數(shù)調(diào)用的執(zhí)行順序與它們分別所屬的defer語句的出現(xiàn)順序(更嚴(yán)謹(jǐn)?shù)卣f,是執(zhí)行順序)完全相反嗡髓。

?當(dāng)一個函數(shù)即將結(jié)束執(zhí)行時,其中的寫在最下邊的defer函數(shù)調(diào)用會最先執(zhí)行收津,其次是寫在它上邊饿这、與它的距離最近的那個defer函數(shù)調(diào)用,以此類推撞秋,最上邊的defer函數(shù)調(diào)用會最后一個執(zhí)行长捧。

?如果函數(shù)中有一條for語句,并且這條for語句中包含了一個defer語句吻贿,那么顯然這條defer語句的執(zhí)行次數(shù)串结,就取決于for語句的迭代次數(shù)。

?并且舅列,同一條defer語句每被執(zhí)行一次肌割,其中的defer函數(shù)調(diào)用就會產(chǎn)生一次,并且帐要,這些函數(shù)調(diào)用同樣不會被立即執(zhí)行把敞。

?那么for語句中的多個defer函數(shù)的執(zhí)行順序:在defer語句每次執(zhí)行的時候,Go語句會把它攜帶的defer函數(shù)及其參數(shù)值另行存儲到一個隊列中榨惠。這個隊列與該defer語句所屬的函數(shù)時對應(yīng)的奋早,并且,它是先進(jìn)后出(FILO)的赠橙,相當(dāng)于一個棧耽装。在需要執(zhí)行某個函數(shù)中的defer函數(shù)調(diào)用的時候,Go語言會先拿到對應(yīng)的隊列期揪,然后從該隊列中一個一個地取出defer函數(shù)及其參數(shù)值掉奄,并逐個執(zhí)行調(diào)用。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末横侦,一起剝皮案震驚了整個濱河市挥萌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌枉侧,老刑警劉巖引瀑,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異榨馁,居然都是意外死亡憨栽,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屑柔,“玉大人屡萤,你說我怎么就攤上這事〉穑” “怎么了死陆?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長唧瘾。 經(jīng)常有香客問我措译,道長,這世上最難降的妖魔是什么饰序? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任领虹,我火速辦了婚禮,結(jié)果婚禮上求豫,老公的妹妹穿的比我還像新娘塌衰。我一直安慰自己,他們只是感情好蝠嘉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布最疆。 她就那樣靜靜地躺著,像睡著了一般是晨。 火紅的嫁衣襯著肌膚如雪肚菠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天罩缴,我揣著相機(jī)與錄音蚊逢,去河邊找鬼。 笑死箫章,一個胖子當(dāng)著我的面吹牛烙荷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播檬寂,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼终抽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了桶至?” 一聲冷哼從身側(cè)響起昼伴,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎镣屹,沒想到半個月后圃郊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡女蜈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年持舆,在試婚紗的時候發(fā)現(xiàn)自己被綠了色瘩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡逸寓,死狀恐怖居兆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情竹伸,我是刑警寧澤泥栖,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布,位于F島的核電站佩伤,受9級特大地震影響聊倔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜生巡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望见妒。 院中可真熱鬧孤荣,春花似錦、人聲如沸须揣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽耻卡。三九已至疯汁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間卵酪,已是汗流浹背幌蚊。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留溃卡,地道東北人溢豆。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像瘸羡,于是被迫代替她去往敵國和親漩仙。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評論 2 354