Go異常處理機(jī)制panic和recover

來(lái)源:旅途散記
recover
使用panic拋出異常后, 將立即停止當(dāng)前函數(shù)的執(zhí)行并運(yùn)行所有被defer的函數(shù),然后將panic拋向上一層,直至程序crash。但是也可以使用被defer的recover函數(shù)來(lái)捕獲異常阻止程序的崩潰,recover只有被defer后才是有意義的。
func main() {
print(123)
print(456)
panic("throw an error")
print(678) //IDE會(huì)有提示: Unreachable code
}
結(jié)果:
123456panic: throw an error
goroutine 1 [running]:
main.main()
/Users/shuangcui/explore/panicandrecover.go:31 +0x67
使用recover()捕獲異常:
func main() {
print(123)
defer func() {
if err := recover(); err != nil {
print("recover it")
}
}()
print(456)
panic("throw an error")
print(678) //IDE會(huì)有提示: Unreachable code
}
結(jié)果為:
123456recover it
如果有兩個(gè)recover,則捕獲異常的是后一個(gè)
func main() {
print(123)
defer func() {
if err := recover(); err != nil {
print("recover it")
}
}()
defer func() {
if err := recover(); err != nil {
print("復(fù)原!")
}
}()
print(456)
panic("throw an error")
print(678) //IDE會(huì)有提示: Unreachable code
}
結(jié)果為:
123456復(fù)原!
panic之后的任何代碼都不會(huì)繼續(xù)執(zhí)行
前提是panic不在if里面
package main
import "fmt"
func main() {
defer_call()
fmt.Println("333 Helloworld")
}
func defer_call() {
defer func() {
fmt.Println("11111")
}()
defer func() {
fmt.Println("22222")
}()
defer func() {
if r := recover(); r != nil {
fmt.Println("Recover from r : ", r)
}
}()
defer func() {
fmt.Println("33333")
}()
fmt.Println("111 Helloworld")
panic("Panic 1!")
//使用panic拋出異常后, 將立即停止當(dāng)前函數(shù)的執(zhí)行并運(yùn)行所有被defer的函數(shù),然后將panic拋向上一層, 直至程序crash
//但是也可以使用被defer的recover函數(shù)來(lái)捕獲異常阻止程序的崩潰,recover只有被defer后才是有意義的。
panic("Panic 2!") //panic1之后的panic2沒(méi)有任何機(jī)會(huì)會(huì)被執(zhí)行, panic2之后的任何代碼更沒(méi)有任何機(jī)會(huì)被執(zhí)行
fmt.Println("222 Helloworld")
}
輸出為:
111 Helloworld
33333
Recover from r : Panic 1!
22222
11111
333 Helloworld
對(duì)于goroutine中的panic,協(xié)程外面的recover是無(wú)法恢復(fù)的;goroutine中的recover,同樣無(wú)法恢復(fù)協(xié)程外的panic
但協(xié)程中的recover可以恢復(fù)協(xié)程中的panic
package main
import (
"fmt"
"time"
)
func main() {
go func() {
defer func() {
if err := recover(); err != nil {
fmt.Println("recover err:", err)
}
}()
panic("里面出錯(cuò)了")
}()
//panic("外面出錯(cuò)了")
time.Sleep(1 * time.Second)
}
輸出為:
recover err 里面出錯(cuò)了
主方法中的recover,也可以恢復(fù)子方法里的panic
但如果go subfunc(),則同樣無(wú)法捕獲subfunc中的異常
func main() {
fmt.Println(123)
defer fmt.Println(999)
defer func() {
if err := recover(); err != nil {
fmt.Println("恢復(fù)異常:",err)
}
}()
subfunc()
}
func subfunc() {
defer fmt.Println(888)
panic("出現(xiàn)了bug")
defer fmt.Println(456)
}
結(jié)果為:
123
888
恢復(fù)異常: 出現(xiàn)了bug
999
因?yàn)閜anic發(fā)生的時(shí)候,panic函數(shù)后面的語(yǔ)句都不會(huì)執(zhí)行了,所以recover函數(shù)不能放在panic語(yǔ)句后面執(zhí)行,而要放在defer函數(shù)中執(zhí)行。
使用 panic 拋出異常后,函數(shù)執(zhí)行將從調(diào)用 panic 的地方停止,如果函數(shù)內(nèi)有 defer 調(diào)用,則執(zhí)行 defer 后邊的函數(shù)調(diào)用,如果 defer 調(diào)用的函數(shù)中沒(méi)有捕獲異常信息,這個(gè)異常會(huì)沿著函數(shù)調(diào)用棧往上傳遞,直到 main 函數(shù)仍然沒(méi)有捕獲異常,將會(huì)導(dǎo)致程序異常退出
如何區(qū)別使用 panic 和 error 兩種方式?
慣例是:導(dǎo)致關(guān)鍵流程出現(xiàn)不可修復(fù)性錯(cuò)誤的使用 panic ,其他使用 error 。
panic 和 recover 的組合有如下特性:
-
有 panic 沒(méi) recover ,程序宕機(jī)。 -
有 panic 也有 recover ,程序不會(huì)宕機(jī),執(zhí)行完對(duì)應(yīng)的 defer 后,從宕機(jī)點(diǎn)退出當(dāng)前函數(shù)后繼續(xù)執(zhí)行。
recover能捕獲所有錯(cuò)誤嗎?
不能!
Go 有哪些無(wú)法恢復(fù)的致命場(chǎng)景?
像
-
并發(fā)讀寫(xiě) map fatal error: concurrent map read and map write -
堆棧內(nèi)存耗盡(如遞歸)
runtime: goroutine stack exceeds 1000000000-byte limit
runtime: sp=0xc0200e1bf0 stack=[0xc0200e0000, 0xc0400e0000]
fatal error: stack overflow
-
將 nil 函數(shù)作為 goroutine 啟動(dòng) fatal error: go of nil func value -
goroutines 死鎖 fatal error: all goroutines are asleep - deadlock! -
線程超過(guò)設(shè)置的最大限制 fatal error: thread exhaustion -
超出可用內(nèi)存 fatal error: runtime: out of memory
總之 都會(huì)報(bào)fatal error:xxxxxxxx
拓展&參考:
golang panic和recover 實(shí)現(xiàn)原理[1]
Go 學(xué)習(xí)筆記(19)— 函數(shù)(05)[如何觸發(fā) panic、觸發(fā) panic 延遲執(zhí)行、panic 和 recover 的關(guān)系][2]
Go 語(yǔ)言踩坑記——panic 與 recover[3]
參考資料
golang panic和recover 實(shí)現(xiàn)原理: https://blog.csdn.net/u010853261/article/details/102761955
[2]Go 學(xué)習(xí)筆記(19)— 函數(shù)(05)[如何觸發(fā) panic、觸發(fā) panic 延遲執(zhí)行、panic 和 recover 的關(guān)系]: https://blog.csdn.net/wohu1104/article/details/105571916
[3]Go 語(yǔ)言踩坑記——panic 與 recover: https://xiaomi-info.github.io/2020/01/20/go-trample-panic-recover/
想要了解Go更多內(nèi)容,歡迎掃描下方??關(guān)注公眾號(hào),回復(fù)關(guān)鍵詞 [實(shí)戰(zhàn)群] ,就有機(jī)會(huì)進(jìn)群和我們進(jìn)行交流
