Go はエラー パニックをキャプチャし、エラー スタックを出力し、パニック状況をトリガーします。

Recovery は、パニック情報を取得し、コルーチンの制御を取り戻すために使用される関数です。
ユーザーモードでのみパニックをキャプチャできます。これには、パニック関数のアクティブなトリガー、null ポインター値の受動的呼び出し、範囲外のインデックス、スタック オーバーフロー、読み取り専用メモリの書き換えなどがあります。
Fatalthrow や Fatalpanic などのパニックは、終了を直接呼び出すため、捕捉できませんexit()

エラーパニックをキャプチャし、エラースタックを出力

func TestPanic(t *testing.T) {
    
    
	j := 0
	for i := 0; i <= 10; i++ {
    
    
		func() {
    
     // try except
			defer func() {
    
    
				if err := recover(); err != nil {
    
    
					fmt.Println("panic recover!")
					// 打印错误和堆栈
					fmt.Printf("err=%v, stack=%s\n", err, string(debug.Stack()))
				}
			}()
			fmt.Printf("%v\n", i)
			x := i / j // 除0报错
			fmt.Printf("%v", x)
		}()
	}
}

上記のコードは、recover がエラーをキャッチできるように、匿名関数を通じて遅延コードとエラー コードをコルーチンに配置します。
Panic は現在の goroutine の遅延のみをトリガーし、recover は defer で呼び出された場合にのみ有効になります。Panic では、複数の呼び出しを defer にネストできます。

コルーチン間で無効化を延期する

func TestPanic1(t *testing.T) {
    
    
	// 跨协程失效,下面一行的defer不会执行
	defer println("in main")

	go func() {
    
    
		defer println("in goroutine")
		fmt.Println("子协程running")
		panic("子协程崩溃")
	}()

	time.Sleep(1 * time.Second)
}

サブコルーチンの defer とメイン コルーチンの defer は同じコルーチン内にないため、サブ コルーチンがパニックになった後は、メイン スレッドの defer は実行されません。

リカバリはパニックが発生した後にのみ有効になります

func TestPanic2(t *testing.T) {
    
    
	defer fmt.Println("in main")
	if err := recover(); err != nil {
    
    
		fmt.Println("occur error")
		fmt.Println(err)
	}

	panic("unknown err")
}

上記のコードでは、エラーが送信される前に、recover が呼び出されているため、recover がエラーをキャッチできません。正しい書き方は、これを defer に入れて呼び出しを遅らせることです。

func TestPanic3(t *testing.T) {
    
    
	defer fmt.Println("in main")
	defer func() {
    
    
		if err := recover(); err != nil {
    
    
			fmt.Println("occur error")
			fmt.Println(err)
		}
	}()

	panic("unknown err")
}

リカバリの役割は、パニックをキャプチャし、それによって通常のコード実行を復元することです。
Recovery は Defer と一緒に使用する必要があります。
cover はパラメータを渡しませんが、戻り値を持ちます。戻り値は、panic によって渡された値です。

パニックのネストされた呼び出しシーケンス

func TestPanic4(t *testing.T) {
    
    
	defer fmt.Println("in main")
	defer func() {
    
    
		defer func() {
    
    
			panic("panic again and again")
		}()
		panic("panic again")
	}()
	// 主动触发panic
	panic("panic once")
}

// in main
// --- FAIL: TestPanic4 (0.00s)
// panic: panic once
//         panic: panic again
//         panic: panic again and again [recovered]
//         panic: panic again and again

関数 F でパニック ステートメントが記述されてトリガーされると、実行される後続のコードは終了します。パニックが発生した関数 F に実行される遅延関数のリストがある場合は、遅延書き込み順序の逆の順序で実行され、関数 G が関数 F を呼び出すと、関数 F は に戻ります
。パニック後の呼び出し側関数 G。関数 G では、関数 F を呼び出すステートメント以降のステートメントは実行されません。関数 G に実行される defer 関数のリストがある場合は、defer の書き込み順序の逆の順序に従っても問題ありません。
ゴルーチン全体を終了してエラーを報告します。

回復は最後のパニックのみをキャプチャします

func TestPanic5(t *testing.T) {
    
    
	defer func() {
    
    
		if err := recover(); err != nil {
    
    
			fmt.Println(err)
		}
	}()
	defer func() {
    
    
		panic("three")
	}()
	defer func() {
    
    
		panic("two")
	}()
	panic("one")
	// three
}

一般的なトリガーパニック状況

配列の添字が範囲外です (実行時エラー)

func TestPanic6(t *testing.T) {
    
    
	var s []string
	fmt.Println(s)
	fmt.Println(s[0])
	// panic: runtime error: index out of range [0] with length 0 [recovered]
	//     panic: runtime error: index out of range [0] with length 0
}

Null ポインタ例外 (実行時エラー)

func TestPanic7(t *testing.T) {
    
    
	type Person struct {
    
    
		Name string
		Age  int
	}
	var p *Person
	fmt.Println(p)
	fmt.Println(p.Name)
	//		panic: runtime error: invalid memory address or nil pointer dereference [recovered]
	//	        panic: runtime error: invalid memory address or nil pointer dereference
	//
	// [signal 0xc0000005 code=0x0 addr=0x0 pc=0xafe05b]
}

型アサーションが失敗しました (インターフェイス変換例外)

func add(a, b interface{
    
    }) {
    
    
	i := a.(int)
	j := b.(int)
	fmt.Println(i + j)
}
func TestPanic8(t *testing.T) {
    
    
	add(20, 18)
	add(1, "hello")
	//	38
	//
	// --- FAIL: TestPanic8 (0.00s)
	// panic: interface conversion: interface {} is string, not int [recovered]
	//
	//	panic: interface conversion: interface {} is string, not int
}

チャネルは空、チャネルは閉じています (データの書き込み)

func TestPanic9(t *testing.T) {
    
    
	var ch chan int
	close(ch)
	// panic: close of nil channel [recovered]
    //     panic: close of nil channel
}

おすすめ

転載: blog.csdn.net/lilongsy/article/details/131381452