久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

Go編程中recover源碼是什么

共計(jì) 3009 個(gè)字符,預(yù)計(jì)需要花費(fèi) 8 分鐘才能閱讀完成。

本篇內(nèi)容介紹了“Go 編程中 recover 源碼是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓丸趣 TV 小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

recover 的真身

就像我們之前針對(duì) panic 做的一樣,我們也寫一段簡(jiǎn)單的代碼,通過(guò)其匯編碼嘗試找出內(nèi)置函數(shù) recover() 的底層實(shí)現(xiàn)。

編寫以下簡(jiǎn)單的代碼,并保存在名為 compile.go 的文件里:

// recover/compile.go
package recover
func compile() {defer func() {recover()
}

然后使用以下命令編譯代碼:

go tool compile -S recover/compile.go

接著根據(jù)代碼行號(hào)找出 recover() 語(yǔ)句對(duì)應(yīng)的匯編碼:

0x0024 00036 (recover/compile.go:5) PCDATA $0, $1
0x0024 00036 (recover/compile.go:5) PCDATA $1, $0
0x0024 00036 (recover/compile.go:5) LEAQ  ..fp+40(SP), AX
0x0029 00041 (recover/compile.go:5) PCDATA $0, $0
0x0029 00041 (recover/compile.go:5) MOVQ AX, (SP)
0x002d 00045 (recover/compile.go:5) CALL runtime.gorecover(SB)

我們可以看到 recover() 函數(shù)調(diào)用被替換成了 runtime.gorecover() 函數(shù)。runtime.gorecover() 實(shí)現(xiàn)源碼位于 src/runtime/panic.go。

gorecover()

runtime.gorecover() 函數(shù)實(shí)現(xiàn)很簡(jiǎn)短:

func gorecover(argp uintptr) interface{} {gp := getg()
 //  獲取 panic 實(shí)例,只有發(fā)生了 panic,實(shí)例才不為 nil
 p := gp._panic
 // recover 限制條件
 if p != nil   !p.goexit   !p.recovered   argp == uintptr(p.argp) {
 p.recovered = true
 return p.arg
 return nil
}

短短的代碼,蘊(yùn)含的信息量卻很大。它可以解釋以下問(wèn)題:

recover() 到底是如何恢復(fù) panic 的?

為什么 recover() 一定要在 defer() 函數(shù)中才生效?

假如 defer() 函數(shù)中調(diào)用了函數(shù) A(),為什么 A() 中的 recover() 不能生效?

恢復(fù)邏輯

runtime.gorecover() 函數(shù)通過(guò)協(xié)程數(shù)據(jù)結(jié)構(gòu)中的_panic 得到當(dāng)前的 panic 的實(shí)例(上面代碼中 p),如果當(dāng)前 panic 的狀態(tài)支持 recover,給該 panic 實(shí)例標(biāo)記 recovered 狀態(tài)(p.recovered = true),最后返回 panic() 函數(shù)的參數(shù)(p.arg)。

另外,當(dāng)前執(zhí)行 recover() 的 defer 函數(shù)是被 runtime.gopanic() 執(zhí)行的,defer 函數(shù)執(zhí)行結(jié)束以后,runtime.gopanic() 函數(shù)中會(huì)檢查 panic 實(shí)例的 recovered 狀態(tài),如果發(fā)現(xiàn) panic 被恢復(fù),runtime.gopanic() 將會(huì)結(jié)束當(dāng)前 panic 流程,將程序流程恢復(fù)正常。

生效條件

通過(guò)代碼的 if 語(yǔ)句可以看到需要滿足四個(gè)條件才可以恢復(fù) panic,且四個(gè)條件缺一不可:

p != nil:必須存在 panic;

!p.goexit:非 runtime.Goexit();

!p.recovered:panic 還未被恢復(fù);

argp == uintptr(p.argp):recover() 必須被 defer() 直接調(diào)用。

當(dāng)前協(xié)程沒(méi)有產(chǎn)生 panic 時(shí),協(xié)程結(jié)構(gòu)體中 panic 的鏈表為空,不滿足恢復(fù)條件。

當(dāng)程序運(yùn)行 runtime.Goexit() 時(shí)也會(huì)創(chuàng)建一個(gè) panic 實(shí)例,會(huì)標(biāo)記該實(shí)例的 goexit 屬性為 true,但該類型的 panic 不能被恢復(fù)。

假設(shè)函數(shù)包含多個(gè) defer 函數(shù),前面的 defer 通過(guò) recover() 消除 panic 后,函數(shù)中剩余的 defer 仍然會(huì)執(zhí)行,但不能再次 recover(),如下代碼所示,函數(shù)第一行 defer 中的 recover() 將返回 nil。

func foo() {defer func() {recover()}() //  恢復(fù)無(wú)效,因?yàn)開(kāi)panic.recovered = true
 defer func() {recover()}() //  標(biāo)記_panic.recovered = true
 panic(err)
}

細(xì)心的讀者或許會(huì)發(fā)現(xiàn),內(nèi)置函數(shù) recover() 沒(méi)有參數(shù),runtime.gorecover() 函數(shù)卻有參數(shù),為什么呢?這正是為了限制 recover() 必須被 defer() 直接調(diào)用。

runtime.gorecover() 函數(shù)的參數(shù)為調(diào)用 recover() 函數(shù)的參數(shù)地址,通常是 defer 函數(shù)的參數(shù)地址,同地_panic 實(shí)例中也保存了當(dāng)前 defer 函數(shù)的參數(shù)地址,如果二者一致,說(shuō)明 recover() 被 defer 函數(shù)直接調(diào)用。舉例如下:

func foo() {defer func() { //  假設(shè)函數(shù)為 A
 func() { //  假設(shè)函數(shù)為 B
 // runtime.gorecover(B),傳入函數(shù) B 的參數(shù)地址
 // argp == uintptr(p.argp)  檢測(cè)失敗,無(wú)法恢復(fù)
 if err := recover(); err != nil { 
 fmt.Println(A)
}

設(shè)計(jì)思路

通過(guò)以上源碼的分析,我們可以很好地回答以下問(wèn)題了:

為什么 recover() 一定要在 defer() 函數(shù)中才生效?

假如 defer() 函數(shù)中調(diào)用了函數(shù) A(),為什么 A() 中的 recover() 不能生效?

如果 recover() 不在 defer() 函數(shù)中,那么 recover() 可能出現(xiàn)在 panic() 之前,也可能出現(xiàn)在 panic() 之后,出現(xiàn)在 panic() 之前,因?yàn)檎也坏?panic 實(shí)例而無(wú)法生效,出現(xiàn)在 panic() 之后,代碼沒(méi)有機(jī)會(huì)執(zhí)行,所以 recover() 必須存在于 defer 函數(shù)中才會(huì)生效。

通過(guò)上面的分析,從代碼層面我們理解了為什么 recover() 函數(shù)必須被 defer 直接調(diào)用才會(huì)生效。但為什么要有這樣的設(shè)計(jì)呢?

筆者也沒(méi)有找到官方關(guān)于此設(shè)計(jì)的資料,不過(guò)筆者認(rèn)為此設(shè)計(jì)非常合理。

考慮下面的代碼:

func foo() {defer func() {thirdPartPkg.Clean() //  調(diào)用第三方包清理資源
 if err != nil { //  條件不滿足觸發(fā) panic
 panic(xxx)
}

有時(shí)我們會(huì)在代碼里顯式地觸發(fā) panic,同時(shí)往往還會(huì)在 defer 函數(shù)里調(diào)用第三方包清理資源,如果第三方包也使用了 recover(),那么我們觸發(fā)的 panic 將會(huì)被攔截,而且這種攔截可能是非預(yù)期的,并不我們期望的結(jié)果。

“Go 編程中 recover 源碼是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注丸趣 TV 網(wǎng)站,丸趣 TV 小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

正文完
 
丸趣
版權(quán)聲明:本站原創(chuàng)文章,由 丸趣 2023-08-04發(fā)表,共計(jì)3009字。
轉(zhuǎn)載說(shuō)明:除特殊說(shuō)明外本站除技術(shù)相關(guān)以外文章皆由網(wǎng)絡(luò)搜集發(fā)布,轉(zhuǎn)載請(qǐng)注明出處。
評(píng)論(沒(méi)有評(píng)論)
主站蜘蛛池模板: 叶城县| 寻甸| 三亚市| 弋阳县| 普兰店市| 光泽县| 麻栗坡县| 贺州市| 广德县| 秭归县| 根河市| 菏泽市| 乡宁县| 思南县| 孙吴县| 白银市| 青冈县| 香格里拉县| 南漳县| 若尔盖县| 邯郸县| 铜陵市| 莱芜市| 河池市| 上林县| 遵义县| 宜宾市| 鄯善县| 苗栗市| 天气| 大化| 越西县| 五家渠市| 景洪市| 潞西市| 仙游县| 中卫市| 商水县| 兴隆县| 宁南县| 宜丰县|