1. 程式人生 > 實用技巧 >panic 捕獲和 throw 崩潰

panic 捕獲和 throw 崩潰

一、go 語言 panic 報錯捕獲

使用 go 語言的同學在真實專案中應該經常出現空指標使用等 panic 報錯,這類報錯與 C++ 中的 try-catch 模組不同,go 語言會一直將當前 panic 一直從報錯棧傳至最外層的棧,所以很多 go 語言的架構都會在架構中 handler 的入口新增一串程式碼

1     defer func() {
2         if x := recover(); x != nil {
3             // TODO fix panic
4         }
5     }()

這裡講幾個關鍵字

defer:註冊一個回撥函式,在當前棧退出時,按註冊入棧的順序,從最後註冊的 defer 函式開始執行,函式內部發生 panic,屬於可修復型崩潰,所以 go 語言會有序的退出棧,並執行 defer 函式

recover:捕捉 panic 異常,並打斷當前的 panic,進行處理修復,保證不會讓單個 handler 影響到整個程式

上述的異常捕獲方法想必熟悉 go 語言的同學基本都能瞭解。但下面我們瞭解一些 go 語言中無法崩潰和修復的 throw 崩潰

二、go 語言 throw 奔潰

其實 go 語言原始碼中一些地方有一些 throw 呼叫,這個函式會列印相應的 fatal msg,並退出整個程式,因為這類報錯被 go 語言認為無法動態修復的崩潰。所以這類奔潰與 panic 不同,屬於無法通過 defer 和 recover 捕獲的崩潰(因為無法修復),簡單舉兩個栗子

lock:

當使用一個初始化的鎖,並未加鎖就在程式碼中就解鎖,就會發生 throw 崩潰

1 var lock sync.Mutex
2 lock.Unlock()  // fatal: sync: unlock of unlocked mutex
3 
4 
5 // from go 1.91
6 new := atomic.AddInt32(&m.state, -mutexLocked)
7 if (new+mutexLocked)&mutexLocked == 0 {
8     throw("sync: unlock of unlocked mutex") // post a throw
9 }

map:熟悉 go 語言的開發者都知道,在 go 多攜程架構使用便利的情況下,往往存在很多執行緒不安全的變數,map 就是其中最經典的栗子,當在併發下,在沒有新增讀寫鎖的情況下對 map 進行寫、讀寫操作時,也會丟擲 throw 崩潰

testmap := make([int],1)
for i := 0; i < 1000; i++ {
    go func() {
                for true{
                    testmap[1] = 10 // fatal: sync: curcurent map writes
                }    
    }()
}

需要注意的是,這類崩潰是直接 down 掉整個程序的,所以我們線上使用 go 語言進行應用開發時,一定要記得使用 supervisor 之類的程序管理工具,確保進行崩潰後先拉起來,再進行修復。否則會產生大面積機器完全宕機的情況。

再需要注意的一點是,go 語言中一個程序往往有很多 goroutinue 在同時進行,如果發生 throw 奔潰時,整個程序都會被關掉,如果通過日誌,會發現列印了無數堆疊資訊的日誌(所有 goroutinue 的日誌),這時候千萬不要在堆疊日誌上下功夫了,因為打印出來的都是正常日誌,只需要檢視日誌中的 fatal 關鍵字即可找出真正的問題所在。

轉自:https://www.cnblogs.com/novice-dxx/p/11938681.html