panic和recover

有些错误编译期就能发现,编译的时候编译器就会报错,而有些运行时的错误编译器是没办法发现的。golang的运行时异常叫做panic

在golang当切片访问越界,空指针引用等会引起panicpanic如果没有自己手动捕获的话,程序会中断运行并打印panic信息。

var a []int = []int{0, 1, 2}
fmt.Println(a[3]) //panic: runtime error: index out of range [3] with length 3
type A struct {
		Name string
	}

var a *A
fmt.Println(a.Name) //panic: runtime error: invalid memory address or nil pointer dereference

手动panic

当程序运行中出现一些异常我们需要程序中断的时候我们可以手动panic

var model = os.Getenv("MODEL") //获取环境变量 MODEL
if model == "" {
    panic("no value for $MODEL") //MODEL 环境变量未设置程序中断
}

使用recover捕获panic

我们在defer修饰的函数里面使用recover可以捕获的panic

func main() {
	func() {
		defer func() {
			if err := recover(); err != nil { //recover 函数没有异常的是返回 nil
				fmt.Printf("panic: %v ", err)
			}
		}()
		func() {
			panic("panic") //函数执行出现异常
		}()
	}()

	fmt.Println("here") 
}

执行结果是

anic: panic
here

panic的产生后会终止当前函数运行,然后去检测当前函数的defer是否有recover,没有的话会一直往上层冒泡直至最顶层;如果中间某个函数的defer有recover则这个向上冒泡过程到这个函数就会终止。

golang中goroutine没有父子关系,不能在一个goroutine中 recover另一个goroutinepanic

func main() {
	func() {
		defer func() {
			if err := recover(); err != nil { 
				fmt.Printf("panic: %v ", err)
			}
		}()
		go func() {
			panic("panic")
		}()
	}()
	time.Sleep(1e9) 
	fmt.Println("here")
}

执行结果是

panic: panic

goroutine 6 [running]:
main.main.func1.2()
	/workspace/gocode/test/cmd/test.go:16 +0x39
created by main.main.func1
	/workspace/gocode/test/cmd/test.go:15 +0x57
exit status 2

我们看到虽然recover 捕获了错误信息,但是程序还是退出了。