上個星期的文章說明了recover的一個問題静陈,不過當時給下的結論是因為defer的問題燕雁,今天特此再發(fā)一篇文章诞丽,算是勘誤了。
重新回顧下上回的問題:
func main() {
recoverMode4()
}
func recoverMode1() {
defer func() { // 有效的捕獲
if r := recover();r != nil{
fmt.Println(r)
}
}()
panic("this is a panic!")
}
func recoverMode2() {
defer MyRecover() // 有效的捕獲
panic("this is a panic!")
}
func recoverMode3() {
defer MyRecover() // 無效的捕獲
go func() {
panic("this is a panic!")
}()
}
func recoverMode4() {
defer MyRecover2() // 無效的捕獲
panic("this is a panic!")
}
func MyRecover() {
if r := recover();r != nil{
fmt.Println(r)
}
}
func MyRecover2() {
func() {
if r := recover();r != nil{
fmt.Println(r)
}
}()
}
上面的代碼分別展示了四中recover的類型拐格,第一種是我們平常使用的僧免,也是正常的方法,是能捕獲成功的捏浊;第二種其實和第一種沒有太大的區(qū)別懂衩,它的出現(xiàn)是為了引出第四種,和第四種相對應金踪;第三種是不會捕獲成功的浊洞,這里展示了recover了第一個知識點:不能捕獲不在同一個groutine內發(fā)生的panic;第四種和第二種方法相對應胡岔,但是他也無法捕獲成功法希,應為其中的函數(shù)調用棧的原因。下面從recover的源碼角度分析靶瘸,看看究竟:
func gorecover(argp uintptr) interface{} {
// Must be in a function running as part of a deferred call during the panic.
// Must be called from the topmost function of the call
// (the function used in the defer statement).
// p.argp is the argument pointer of that topmost deferred function call.
// Compare against argp reported by caller.
// If they match, the caller is the one who can recover.
gp := getg()
p := gp._panic
if p != nil && !p.goexit && !p.recovered && argp == uintptr(p.argp) {
p.recovered = true
return p.arg
}
return nil
上面的就是recover的源碼苫亦,代碼很簡單,首先是getg奕锌,這個就是獲取獲取當期那groutine著觉,這也就解釋了上面的第三個判斷為啥沒辦法捕獲成功的原因,最后就是幾個簡單的判斷惊暴,不過這幾個簡單的判斷卻是學問很大的饼丘。前三個判斷都很好理解,分別判斷了是否產(chǎn)生panic辽话,當前函數(shù)是否已經(jīng)退出和是否已經(jīng)被修復肄鸽。最后一個是當前參數(shù)和當前groutine的函數(shù)指針的判斷,這個判斷解釋了上面的第四個問題油啤,其中參數(shù)p.argp是當期最上層函數(shù)調用defer的函數(shù)指針典徘,argp是調用recover的函數(shù)指針,在函數(shù)調用上益咬,若是上面的第四種方法逮诲,p.argp指向的是MyRecover2函數(shù)內的func,注意這里并不是MyRecover2幽告,argp指向的是recoverMode4梅鹦。這兩者不想等,無法recover成功冗锁。
如上圖所示齐唆,panic和recover之間必須隔著一層函數(shù)調用,沒有這層函數(shù)調用冻河,或是大于一層的函數(shù)調用箍邮,recover都會失敗茉帅。
今天的課就到這里結束了,本節(jié)課分析了recover的失敗的兩個問題:
1锭弊、是不在同一個groutine的recover會失敗
2堪澎、recover和最上層的函數(shù)調用,中間必須隔著且僅僅是一層函數(shù)調用味滞,不然也會失斎狻!
下課桃犬!