Go編程中recover源碼是什么

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

企業(yè)建站必須是能夠以充分展現(xiàn)企業(yè)形象為主要目的,是企業(yè)文化與產(chǎn)品對外擴展宣傳的重要窗口,一個合格的網(wǎng)站不僅僅能為公司帶來巨大的互聯(lián)網(wǎng)上的收集和信息發(fā)布平臺,創(chuàng)新互聯(lián)面向各種領(lǐng)域:成都邊坡防護網(wǎng)成都網(wǎng)站設(shè)計、成都全網(wǎng)營銷推廣解決方案、網(wǎng)站設(shè)計等建站排名服務(wù)。


recover的真身

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

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

// recover/compile.go

package recover

func compile() {
	defer func() {
		recover()
	}()
}

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

go tool compile -S recover/compile.go

接著根據(jù)代碼行號找出recover()語句對應(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()實現(xiàn)源碼位于src/runtime/panic.go。

gorecover()

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

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

短短的代碼,蘊含的信息量卻很大。 它可以解釋以下問題:

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

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

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

恢復邏輯

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

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

生效條件

通過代碼的if語句可以看到需要滿足四個條件才可以恢復panic,且四個條件缺一不可:

  • p != nil:必須存在panic;

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

  • !p.recovered:panic還未被恢復;

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

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

當程序運行runtime.Goexit()時也會創(chuàng)建一個panic實例,會標記該實例的goexit屬性為true,但該類型的panic不能被恢復。

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

func foo() {
	defer func() {recover()}() // 恢復無效,因為_panic.recovered = true
	defer func() {recover()}() // 標記_panic.recovered = true
	panic("err")
}

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

runtime.gorecover()函數(shù)的參數(shù)為調(diào)用recover()函數(shù)的參數(shù)地址,通常是defer函數(shù)的參數(shù)地址,同地_panic實例中也保存了當前defer函數(shù)的參數(shù)地址,如果二者一致,說明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) 檢測失敗,無法恢復
			if err := recover(); err != nil { 
				fmt.Println("A")
			}
		}()
	}()
}
設(shè)計思路

通過以上源碼的分析,我們可以很好地回答以下問題了:

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

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

如果recover()不在defer()函數(shù)中,那么recover()可能出現(xiàn)在panic()之前,也可能出現(xiàn)在panic()之后,出現(xiàn)在panic()之前,因為找不到panic實例而無法生效,出現(xiàn)在panic()之后,代碼沒有機會執(zhí)行,所以recover()必須存在于defer函數(shù)中才會生效。

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

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

考慮下面的代碼:

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

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

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

分享文章:Go編程中recover源碼是什么
本文網(wǎng)址:http://www.muchs.cn/article14/jpjjde.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站營銷、企業(yè)建站、虛擬主機品牌網(wǎng)站建設(shè)、小程序開發(fā)App設(shè)計

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都app開發(fā)公司