天天看點

Go panic & recover 使用注意點:

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()無法繼續正常執行列印
}
           

錯誤示例 - 運作結果:

Go panic & recover 使用注意點:

正确示例: 

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的錯誤

                                                                                     我寫部落格是真的慢...寫了好久~