[Go] panic、recover

120 阅读1分钟

一、panic()

通过panic()可以手动触发宕机,此时会让程序崩溃,并将堆栈和 goroutine 信息输出到控制台。

panic 特性:

  • panic()前如果有defer语句,则会在宕机退出前先把defer语句执行完;
  • panic()后的语句不会再继续执行。

例1

package main  
  
import "fmt"  
  
func main() {  
    fmt.Println("before")  
    panic("panic")  
    fmt.Println("after")   
}

控制台:

image.png

例2

func main() {  
   defer fmt.Println("宕机1")  
   defer fmt.Println("宕机2") 
   
   panic("panic")
   
   defer fmt.Println("宕机3")  
   defer fmt.Println("宕机4")  
}

image.png

二、recover()

panic() 抛出的错误可被 recover() 捕捉到。

package main

import (
	"fmt"
	"runtime"
)

type panicContext struct {
	function string // 所在函数
}

func foo(bar func()) {

	// 延迟处理的函数.注意整体的执行顺序为:bar()正常语句 -> panic() -> defer 函数内 recover() 等 -> 正式宕机
	defer func() {
		err := recover()

		switch err.(type) {
		case runtime.Error:
			fmt.Println("runtime error:",err)
		default:
			fmt.Println("error:",err)
		}

	}()

	// 用来触发 panic
	bar()
}

func main() {
	fmt.Println("运行前")

	// 调用两次
	// 第一次,手动触发 panic
	foo(func() {
		fmt.Println("手动宕机前")

		// 使用 panic() 传递给 recover() 上下文
		panic(&panicContext{
			"手动触发 panic",
		})

		fmt.Println("手动宕机后")
	})

	// 第二次,故意空指针访问造成运行时错误
	foo(func() {
		fmt.Println("空指针运行时宕机前")

		var a *int
		*a =1

		fmt.Println("空指针运行时宕机后")

	})

	fmt.Println("运行后")
}

image.png

总结:

  1. 从未执行
fmt.Println("手动宕机后") 和
fmt.Println("空指针运行时宕机后")

可知一个函数内panic后的语句不再执行。
2. 执行了

fmt.Println("运行后")

可知,panic 配合 recover 不会让程序宕机,而是继续执行。