golang recover函數使用中的一些坑解析
目錄
- 正文
- 一,正常情況下
- 二, goroutine中panic
- 三,間接調用recover
- 四,nil panic
- 五,總結
正文
眾所周知golang 中recover函數可以捕捉panic,防止在出現異常的情況下服務整個不可用。然而某些情況下recover也無法catch panic。下面就會說一些這些情況。
一,正常情況下
package?main import?"fmt" func?main(){ ????defer?func(){ ????????if?err?:=?recover();err?!=?nil{ ????????????fmt.Printf("err?=?%v",err) ????????} ????}() ????panic("a?panic") } 打印結果: err?=?a?panic Process?finished?with?exit?code?0
能正常catch panic
二, goroutine中panic
之前線上環境出現過接口出現panic導致服務不可用的情況,于是同事就直接在main函數加了個recover認為萬事無憂了。實際上recover并不能捕捉到協程中的panic。
package?main import?"fmt" func?main(){ ????defer?func(){ ????????if?err?:=?recover();err?!=?nil{ ????????????fmt.Printf("err?=?%v",err) ????????} ????}() ????go?func(){ ????????panic("a?panic") ????}() ????select{} } 打印結果: panic:?a?panic goroutine?6?[running]: main.main.func2() ????I:/goProject/catchPanic.go:13?+0x40 created?by?main.main ????I:/goProject/catchPanic.go:12?+0x5e
實際上還是會panic導致服務不可用。
正確寫法
package?main import?"fmt" func?main(){ ????go?func(){ ????????defer?func(){ ????????????if?err?:=?recover();err?!=?nil{ ????????????????fmt.Printf("err?=?%v",err) ????????????} ????????}() ????????panic("a?panic") ????}() ????select?{} } 返回值: fatal?error:?all?goroutines?are?asleep?-?deadlock! goroutine?1?[select?(no?cases)]: main.main() ????I:/goProject/catchPanic.go:15?+0x41 err?=?a?panic Process?finished?with?exit?code?2
可以看到panic被正常捕捉,同時因為select語句陷入阻塞,報了一個死鎖的錯。
三,間接調用recover
在我想要把recover封裝成成一個函數的時候,發現recover并沒有生效,因為recover只有在被defer語句直接調用的時候才會生效。當recover在其他函數內部的時候無法正確捕捉到panic。
package?main import?"fmt" func?main(){ ????defer?cover() ????panic("a?panic") } func?cover(){ ????defer?func(){ ????????if?err?:=?recover();err!=?nil{ ????????????fmt.Println(err) ????????} ????}() } 返回值: panic:?a?panic goroutine?1?[running]: main.main() ????I:/goProject/catchPanic.go:7?+0x62
四,nil panic
panic要被捕捉,還需要滿足一種條件,就是panic不是nil panic,否則在進行捕獲判斷的時候無法知道是panic沒有發生還是panic本身就是nil。
例如以下代碼
package?main import?"fmt" func?main()?{ ????defer?func(){ ????????if?err?:=?recover();err?!=?nil{ ????????????fmt.Println(err) ????????} ????????fmt.Println("after?recover") ????}() ????panic(nil) ????select{} } 返回值: after?recover
recover并沒有正確處理異常,因為異常的值為nil。
五,總結
這篇文章講述了三種recover會失效的情況。
- 攜程中出現panic
- defer不直接調用recover
- panic的值為nil值
寫代碼的時候需要注意避免因為這幾種情況的出現而導致服務不可用。以上就是golang新手常遇見的一些坑。
以上就是golang recover函數使用中的一些坑解析的詳細內容,更多關于golang recover函數坑的資料請關注其它相關文章!