独特的panic和recover

1 panic

Go 语言的错误处理方法比较特别,需要返回error给调用者,但是如果遇到的是无法恢复的错误,返回error也没有意义,此时就需要主动触发panic

但有些错误只能在运行时检查,如数组访问越界、空指针引用等,这些运行时错误会引发panic异常。

panic能够改变程序的控制流,调用panic后会立刻停止执行当前函数的剩余代码,并在当前 Goroutine 中递归执行调用方defer

一个简单的例子:

func main() {
	defer fmt.Println("defer 1")
	defer fmt.Println("defer 2")

	go func() {
		defer fmt.Println("defer 4")
	}()

	panic("panic")

	defer fmt.Println("defer 3")
}

$ go run main.go
defer 2
defer 1
panic: panic

goroutine 1 [running]:
main.main()
	/home/user/go/src/panic/main.go:13 +0xfa
exit status 2

panic仅保证当前 Goroutine 的panic语句前的所有defer能被执行,其他 Goroutine 的defer不一定执行。

嵌套崩溃

panic是可以多次嵌套调用的。

func main() {
	defer fmt.Println("defer 1")

	defer func() {
		defer func() {
			panic("panic 3")
		}()
		panic("panic 2")
	}()

	panic("panic 1")
}

$ go run main.go
defer 1
panic: panic 1
	panic: panic 2
	panic: panic 3

goroutine 1 [running]:
main.main.func1.1()
...
exit status 2

从上述程序输出的结果,我们可以确定程序多次调用panic也不会影响defer函数的正常执行,所以用defer进行收尾工作一般来说都是安全的。

2 recover

一般情况下,对于主动触发的panic不应该做任何处理,但有时,一些异常是可以进行处理的,或者可能需要在程序中止前做一操作。

recover关键字的作用就是这个,能使程序从panic中恢复,并返回panic的值。

recover只有在defer中调用才能生效。

func test() int {
	defer fmt.Println("defer")

	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	panic("panic")

	return 1
}

func main() {
	fmt.Println(test())
	fmt.Println("main is running")
}

$ go run main.go
panic
defer
0

recover没有让程序异常退出,且defer正常执行,panic后的代码没有执行,test()函数返回了零值。

3 panic+recover简化错误处理

func catchErr(num int) {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("[recover]", err, num)
		}
	}()

	panic("panic ")
}

func main() {
	defer func() {
		if err := recover(); err != nil {
			fmt.Println("main recover: ", err)
		}
	}()

	for i := 0; i < 3; i++ {
		fmt.Println("main goroutine: ", i)
		go catchErr(i)
		time.Sleep(time.Second)
	}

start:
	goto start
}

$ go run main.go
main goroutine:  0
[recover] panic  0
main goroutine:  1
[recover] panic  1
main goroutine:  2
[recover] panic  2

为每个goroutine添加一个可以自动消化panicrecover,能避免整个程序的异常中断。

上一篇:[golang] 错误处理


下一篇:Go【No-14】错误与异常