上個星期的文章說明了recover的一個問題,不過當(dāng)時給下的結(jié)論是因為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)是為了引出第四種,和第四種相對應(yīng);第三種是不會捕獲成功的,這里展示了recover了第一個知識點:不能捕獲不在同一個groutine內(nèi)發(fā)生的panic;第四種和第二種方法相對應(yīng),但是他也無法捕獲成功,應(yīng)為其中的函數(shù)調(diào)用棧的原因。下面從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,這個就是獲取獲取當(dāng)期那groutine,這也就解釋了上面的第三個判斷為啥沒辦法捕獲成功的原因,最后就是幾個簡單的判斷,不過這幾個簡單的判斷卻是學(xué)問很大的。前三個判斷都很好理解,分別判斷了是否產(chǎn)生panic,當(dāng)前函數(shù)是否已經(jīng)退出和是否已經(jīng)被修復(fù)。最后一個是當(dāng)前參數(shù)和當(dāng)前groutine的函數(shù)指針的判斷,這個判斷解釋了上面的第四個問題,其中參數(shù)p.argp是當(dāng)期最上層函數(shù)調(diào)用defer的函數(shù)指針,argp是調(diào)用recover的函數(shù)指針,在函數(shù)調(diào)用上,若是上面的第四種方法,p.argp指向的是MyRecover2函數(shù)內(nèi)的func,注意這里并不是MyRecover2,argp指向的是recoverMode4。這兩者不想等,無法recover成功。

如上圖所示,panic和recover之間必須隔著一層函數(shù)調(diào)用,沒有這層函數(shù)調(diào)用,或是大于一層的函數(shù)調(diào)用,recover都會失敗。
今天的課就到這里結(jié)束了,本節(jié)課分析了recover的失敗的兩個問題:
1、是不在同一個groutine的recover會失敗
2、recover和最上層的函數(shù)調(diào)用,中間必須隔著且僅僅是一層函數(shù)調(diào)用,不然也會失敗!
下課!