Panic and Recover
[TOC]
1. Panic ,recover关系
在go中处理不正常状况的最惯用的方法是使用 errors ,错误可以满足大多数的异常处理场景。但是还是有很多状况,使得程序不可以简单地在一个异常后继续执行。在这时就需要一个panic来终止程序。当一个panic发生时,程序的运行终止,所有的deferred操作都被依次执行,之后程序的控制权返回给调用者。该过程持续进行直到当前goroutines 的所有函数都返回。这时程序根据栈顺序(stack trace)打印panix信息然后终止。
也可以通过recover操作从一个panic中恢复该goroutine。panic以及recover可以被认为与 try-catch-finally 的操作机制相似。可以使用这种方法使得程序更加简洁。
2. 使用panic
注意:需要特别谨慎使用panic,只有当程序无法继续执行时才可以使用panic,否则使用errors进行处理。一般具有以下两种使用panic的样例:
无法恢复的错误,使得程序无法简单的继续执行。一个例子:当一个web服务器没有成功绑定到一个要求的端口,此时就需要使用一个panic因为绑定失败无法进行下一部动作
编程错误。当有一个method接受一个指针类型的接收器,但是使用一个nil类型变量调用该方法。
2.1 panic使用
func panic(v interface{})
内置函数panic,当一个函数F调用panic函数时,停止当前进程的正常执行,F立即停止执行。被F deferred 的函数按栈顺序正常执行。之后函数F返回到调用者。package main
import (
"fmt"
)
func fullName(firstName *string, lastName *string) {
if firstName == nil {
panic("runtime error: first name cannot be nil")
}
if lastName == nil {
panic("runtime error: last name cannot be nil")
}
fmt.Printf("%s %s\n", *firstName, *lastName)
fmt.Println("returned normally from fullName")
}
func main() {
firstName := "Elon"
fullName(&firstName, nil)
fmt.Println("returned normally from main")
}
/*panic: runtime error: last name cannot be nil
goroutine 1 [running]:
main.fullName(0x1040c128, 0x0)
/tmp/sandbox135038844/main.go:12 +0x120
main.main()
/tmp/sandbox135038844/main.go:20 +0x80*/在使用fullName式产生了panic,一个运行时错误,此时程序的运行终止。根据函数的调用栈,依次打印main.fullName, main.main() 分别代表maingoroutine的fullName, main方法。
2.2 panicking 中使用defer
使用defer语句,当一个panic发生时,在程序返回之前一定会调用defer指向的语句,相当于其他语言中的finally。
调用fullname中使用了defer语句,所以先出栈,先执行该语句,然后是main中的,最后打印panic信息以及调用栈。
3. Recover
注意使用recover时必须在一个deferred函数中使用。
函数recoverName()调用了recover()函数,返回值传递给panic的调用者。现在发生了panic,然后按栈顺序调用deferred函数,所以先执行recoverName()函数,该函数使用了recover()返回给main.main然后程序继续执行,所以打印最后的语句;然后执行下一个deferred函数。
3.1 panic , recover, goroutines
如果使用不同goroutines的recover函数,不可以使得当前goroutine恢复。
以上代码中,在goroutine b中发生了panic但是使用a中的recover函数无法进行恢复。在a中等待1s使得程序不会在a中中断。如果将go b()改为b()那么程序就可以从panic中恢复。、
4. 运行时错误
panic也可能发生在运行时刻,比如数组越界,变量除0错(注意直接硬码除0错可以被编译器检测,属于语法错误)。关于运行时错误,等价于调用内置函数panic,使用一个runtime.Error类型的参数。有关runtime.Error接口的定义如下:
该接口嵌套了error接口,所以可以使用fmt.Print函数隐式调用Error()方法输出错误信息。RuntimeError()方法没有操作,只是用来区分运行时错误以及普通错误。
运行时错误,也可以使用recover函数进行恢复。
4.1 获取栈信息
如果从一个panic中恢复了goroutine,那么就会失去错误的栈信息(stack trace),可以使用runtime/debug包中的PrintStack()函数获取相应的栈信息。
Last updated
Was this helpful?