1. panic:
一般表示程式出現了嚴重故障,此時程式不能繼續運作,比如服務挂了...
産生方式/場景:
(1) 主動調用:通過 panic() 函數;
(2) 被動調用:panic産生後,會将堆棧資訊抛出來,友善定位問題。
例如:程式在運作階段發生了記憶體異常操作,例如:空指針的取值,索引越界,棧溢出,改寫隻讀記憶體... 等等(還有其他情況會導緻panic,借鑒簡書大佬的連結:Go 常見的引發 panic 的情況)
注意點:
1. panic 其實是一個終止函數棧執行的過程,但是在函數退出前都會執行defer裡面的函數,直到所有的函數都退出後,才會執行panic
2. recover:
recover是go提供的一個用來截獲 panic 資訊,重新擷取協程控制的函數。
使用要求:
1)recover必須在defer函數中使用,但是不能被 defer 直接調用,如:
defer recover() // 直接調用,這樣的寫法不能恢複panic
2)recover隻能恢複同一個協程中的panic,是以必須與可能發生panic的協程在同一個協程中才生效
錯誤示例:
panic在子協程中,而recover在主協程中,recover不能跨協程捕獲panic資訊,最終會導緻所有的協程全部挂掉,程式會整體退出
package main
import (
"fmt"
"runtime/debug"
"time"
)
// 錯誤示例:recover不能跨協程捕獲panic資訊
func Test() {
panic("Test(): 我異常了...溜了溜了")
}
func main() {
defer func() {
err := recover(); if err != nil {
debug.PrintStack()
//fmt.Println(string(debug.Stack()))
}
}()
go Test() // recover不能跨協程捕獲
// Test()
time.Sleep(5 * time.Second) // 模拟執行耗時任務(順便等待子協程執行)
fmt.Println("main()不能正常執行...") // panic後,主協程main()無法繼續正常執行列印
}
錯誤示例 - 運作結果:

正确示例:
package main
import (
"fmt"
"runtime/debug"
"time"
)
// 正确示例:
func Test() {
defer func() {
if err := recover(); err != nil {
debug.PrintStack()
//fmt.Println(string(debug.Stack()))
}
}()
panic("Test(): 我異常了...溜了溜了")
}
func main() {
go Test()
// Test()
time.Sleep(5 * time.Second) // 模拟執行耗時任務(順便等待子協程執行)
fmt.Println("main()依然是能正常執行的...") // 可以正常列印,即使Test()發生panic
}
正确示例 - 運作結果:
Go panic & recover 使用注意點:
3. 函數panic後,列印堆棧調用資訊:
程式發生panic後,可以将堆棧資訊列印出來,友善定位問題:
debug.PrintStack() 或 fmt.Println(string(debug.Stack()))
注意點:
1. 正确使用recover(),要記住recover隻能恢複目前協程的panic,否則還是會導緻整個程序挂掉
2. recover并非萬能的,它隻對使用者态下的panic關鍵字有效
實際上在Go語言中,是存在着一些無法恢複的“恐慌”事件的,如fatalthrow方法、fatalpanic方法等,這些都是直接通過調用 exit() 方法進行中斷的,屬于無法恢複的“恐慌”事件,比如:
對于go1.6以上版本,如果出現 并發map讀寫 程式會直接以 fatal error 崩潰,即使在同一個協程内有recover()也不能恢複
- 如果map由多協程同時讀和寫就會出現 fatal error:concurrent map read and map write的錯誤
- 多個協程同時寫會出現fatal error: concurrent map writes的錯誤
我寫部落格是真的慢...寫了好久~