Golang中panic的異常處理
前言
Golang中當程序發(fā)生致命異常時(比如數(shù)組下標越界,注意這里的異常并不是error),Golang程序會panic(運行時恐慌)。當程序發(fā)生panic時,程序會執(zhí)行當前棧中的defer 函數(shù)列表。然后打印引發(fā)panic的具體信息,最后進程退出,本篇文章我們一起探討Golang中的panic以及如何利用defer 和 recover 來恢復這種致命的異常
分析造成panic堆棧信息
func main() { ?? ?f1() ?? ?fmt.Println("main func end") } func f1() { ?? ?fmt.Println("func f1 start") ?? ?arr := []int{} ?? ?fmt.Println(arr[10]) ?? ?fmt.Println("func f1 end") }
上述代碼中,我在main函數(shù)(主協(xié)程)中調(diào)用了f1函數(shù),在調(diào)用完該函數(shù)后,我打印了「main func end」,程序如果正常執(zhí)行的話會輸出
func f1 start
func f1 end
main func end
很明顯我們可以看出 f1 函數(shù)中,切片arr是沒有索引為10的元素的,這個時候程序運行時會造成panic,下面是程序panic時,console打印的堆棧信息
func f1 start
panic: runtime error: index out of range [10] with length 0
goroutine 1 [running]:
main.f1()
/Users/carlos/go/src/test/demo01.go:15 +0x78
main.main()
/Users/carlos/go/src/test/demo01.go:8 +0x20
Process finished with the exit code 2
我們從堆棧中可以發(fā)現(xiàn):
程序會在造成panic所處的位置終止
我們可以看到錯誤信息中只輸出了 func f1 start
產(chǎn)生panic的原因
panic: runtime error: index out of range [10] with length 0
是哪里造成的panic
goroutine 1 [running] // 運行該程序的協(xié)程
main.f1()
/Users/carlos/go/src/test/demo01.go:15 +0x78 // f1 函數(shù),當前demo01文件的低15行
main.main()
/Users/carlos/go/src/test/demo01.go:8 +0x20 // main 函數(shù),當前文件的弟8行
從上面的panic詳情我們可以看出,錯誤鏈是通過棧的形式展現(xiàn)出來的(mian函數(shù)先調(diào)用,然后在mian中調(diào)用f1),所以大家以后在程序發(fā)生panic時查看堆棧信息時可以先看最上層的錯誤,因為這里是造成panic的根本原因
如何恢復panic造成的程序崩潰
Golang中提供了recover函數(shù)用來恢復因panic造成的程序崩潰。recover函數(shù)有一個返回值來告訴我們panic產(chǎn)生的具體原因。下面我們通過代碼來進行演示
func main() { ?? ?f1() ?? ?r := recover() ?? ?fmt.Printf("%s \n", r) ?? ?fmt.Println("main func end") } func f1() { ?? ?fmt.Println("func f1 start") ?? ?arr := []int{} ?? ?fmt.Println(arr[10]) ?? ?fmt.Println("func f1 end") }
上述代碼中我只是在調(diào)用f1函數(shù)的下一行調(diào)用了recover函數(shù),這樣一來我們的理想狀態(tài)了能夠恢復程序,讓程序執(zhí)行完main函數(shù)中剩下的代碼(打印panic信息,最后打印 main func end),當我們運行該程序的時候發(fā)現(xiàn)recover并沒有起到作用,這是因為當f1造成panic時,f1下方的recover函數(shù)根本沒有機會執(zhí)行。
下面我將上述代碼進行一個簡單的改造:
func main() { ?? ?defer func() { ?? ??? ?fmt.Println("defer func start") ?? ??? ?if r := recover(); r != nil { ?? ??? ??? ?fmt.Printf("%s \n", r) ?? ??? ?} ?? ??? ?fmt.Println("defer func end") ?? ?}() ?? ?f1() ?? ?fmt.Println("main func end") } func f1() { ?? ?fmt.Println("func f1 start") ?? ?arr := []int{} ?? ?fmt.Println(arr[10]) ?? ?fmt.Println("func f1 end") }
輸出
func f1 start
defer func start
runtime error: index out of range [10] with length 0
defer func end
上述代碼中,我只是在main函數(shù)最開頭添加了一個defer 函數(shù),并在該函數(shù)中調(diào)用了recover函數(shù)。注意,我們在文章的最開頭已經(jīng)說明了,當程序發(fā)生panic時,程序會依次執(zhí)行棧中的defer函數(shù)(關(guān)于defer函數(shù)請閱讀官網(wǎng)描述)。所以當前程序發(fā)生panic時在進程退出之前會走到defer函數(shù)中執(zhí)行recover函數(shù),recover函數(shù)會恢復當前進程并打印錯誤信息。
這里我需要特別提醒你一點,最好將defer語句寫在函數(shù)的最前面。如果上述例子我將f1的調(diào)用寫在defer函數(shù)之前,你會發(fā)現(xiàn)recover函數(shù)還是沒有執(zhí)行
func main() { ?? ?f1() ?? ?defer func() { ?? ??? ?fmt.Println("defer func start") ?? ??? ?if r := recover(); r != nil { ?? ??? ??? ?fmt.Printf("%s \n", r) ?? ??? ?} ?? ??? ?fmt.Println("defer func end") ?? ?}() ?? ?fmt.Println("main func end") }
這是因為f1造成panic時,defer函數(shù)根本就沒有壓入函數(shù)調(diào)用棧中。
何時使用panic
當你的項目中特別依賴一些組件時,比如一些web項目中經(jīng)常會在進程啟動之前初始化一些mysql,mq句柄。這些實例對業(yè)務來說是非常重要的,所以當這些實例初始化失敗時我們可以直接讓當前程序panic(手動panic),然后及時發(fā)現(xiàn)問題并解決。這樣總比你帶著問題上線后,然后一批流入打入進來,客戶端瘋狂報錯要好
Golang中手動調(diào)用panic:
func main() { ?? ?initMysql() } func initMysql() { ?? ?panic("init mysql failed") // panic可以接收一個interface類型的參數(shù) }
到此這篇關(guān)于Golang中panic的異常處理的文章就介紹到這了,更多相關(guān)Golang panic內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang替換無法顯示的特殊字符(\u0000,?\000,?^@)
這篇文章主要介紹了golang替換無法顯示的特殊字符,包括的字符有\(zhòng)u0000,?\000,?^@等,下文詳細資料,需要的小伙伴可以參考一下2022-04-04GO excelize讀取excel進行時間類型轉(zhuǎn)換的示例代碼(自動轉(zhuǎn)換)
我們經(jīng)常會遇到如何自動識別excel中的時間類型數(shù)據(jù)并轉(zhuǎn)化成對應的 "Y-m-d H:i:s"類型數(shù)據(jù),本文小編給大家介紹了GO excelize讀取excel進行時間類型轉(zhuǎn)換的示例代碼(自動轉(zhuǎn)換),需要的朋友可以參考下2024-10-10go語言開發(fā)中如何優(yōu)雅得關(guān)閉協(xié)程方法
這篇文章主要為大家介紹了go語言開發(fā)中如何優(yōu)雅得關(guān)閉協(xié)程方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-05-05