Go captures the error panic, prints the error stack, and triggers the panic situation

recover is a function used to capture panic information and regain control of the coroutine.
Can only capture panics in user mode, such as actively triggering panic functions, passively calling null pointer values, index out-of-bounds, stack overflow, rewriting read-only memory, etc.
Panics such as fatalthrow and fatalpanic cannot be caught, because they directly call to exit()exit.

Capture error panic, print error stack

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)
		}()
	}
}

The above code puts the defer and error code into a coroutine through an anonymous function, so that recover can catch the error.
Panic will only trigger the defer of the current goroutine, and recover will only take effect if it is called in defer. Panic allows multiple calls to be nested in defer.

defer invalidation across coroutines

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)
}

The defer in the sub-coroutine and the defer in the main coroutine are not in the same coroutine, so after the sub-coroutine panics, the defer in the main thread will not be executed.

recover will only take effect after a panic occurs

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")
}

In the above code, before the error is sent, recover has been called, which causes recover to fail to catch the error. The correct way to write it is to put it in defer and delay the call.

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")
}

The role of recover is to capture panic, thereby restoring normal code execution.
recover must be used with defer.
recover does not pass in parameters, but has a return value, and the return value is the value passed by panic.

Panic nested call sequence

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

If a panic statement is written and triggered in function F, the subsequent code to be executed will be terminated. If there is a list of defer functions to be executed in the function F where the panic is located, it will be executed in the reverse order of the defer writing order;
if the function G calls the function F, the function F will return to the caller function G after panic. In function G, the statements following the statement calling function F will not be executed. If there is also a list of defer functions to be executed in function G, it is okay to follow the reverse order of defer writing order;
exit the entire goroutine and report an error.

recover will only capture the last panic

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
}

Common trigger panic situations

Array subscript out of bounds (runtime error)

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 pointer exception (runtime error)

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]
}

Type assertion failed (interface conversion exception)

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
}

Channel is empty, channel is closed (write data)

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

Guess you like

Origin blog.csdn.net/lilongsy/article/details/131381452
Recommended