golang panic 和 recover 什么时候用?

golang panic 和 recover 什么时候用?

在通常情况下,函数向其调用方报告错误的方式都是返回一个error类型的值。但是,

当遇到致命错误的时候,很可能会使程序无法继续运行。这时,上述错误处理方式就太

不适合了,Go推荐通过调用panic函数来报告致命错误。

1.panic

为了报告运行期间的致命错误,Go内建了专用函数panic,该函数用于停止当前的

控制流程并引发一个运行时恐慌。它可以接受一个任意类型的参数值,不过这个参数值

的类型常常会是string或者error,因为这样更容易描述运行时恐慌的详细信息。请看下

面的例子:

func main(){
outerFunc()
}

func outerFunc(){
innerFunc()
}

func innerFunc(){
panic(errors.New("An intended fatal errorl"))
}

当调用innerFunc函数中的panic函数后,innerFunc的执行会被停止。紧接着,流程

控制权会交回给调用方 outerFunc函数。然后,outerFunc函数的执行也将被停止。运行时

恐慌就这样沿着调用栈反方向进行传播,直至到达当前goroutine的调用栈的最顶层。一

旦达到顶层,就意味着该goroutine调用栈中所有函数的执行都已经被停止了,程序已经

崩溃。

golang panic 和 recover 什么时候用?

当然,运行时恐慌并不都是通过调用panic函数的方式引发的,也可以由Go的运行

时系统来引发。例如:

myIndex:=4
ia:=[3]int{1,2,3}
_=ia[myIndex]

这个示例中的第3行代码会引发一个运行时恐慌,因为它造成了一个数组访问越界

的运行时错误。这个运行时恐慌就是由Go的运行时系统报告的。它相当于我们显式地调

用panic函数并传入一个runtime.Error类型的参数值。顺便说一句,runtime.Error是一个

接口类型,并且内嵌了Go内置的error接口类型。

显然,我们都不希望程序崩溃。那么,怎样“拦截”一个运行时恐慌呢?

2.recover

运行时恐慌一旦被引发,就会向调用方传播直至程序崩溃。Go提供了专用于“拦截”

运行时恐慌的内建函数recover,它可以使当前的程序从恐慌状态中恢复并重新获得流程

控制权。recover 函数被调用后,会返回一个interface{}类型的结果。如果当时的程序正

处于运行时恐慌的状态,那么这个结果就会是非nil的。

recover 函数应该与defer语句配合起来使用,例如:

defer func(){
ifp:=recover();pl=nil{
fmt.Printf(“Recovered panic:%s\\n",p)
}
}()

把此类代码放在函数体的开始处,这样可以有效防止该函数及其下层调用中的代码

引发运行时恐慌。一旦发现recover函数的调用结果非ni1,就应该采取相应的措施。

值得一提的是,Go标准库中有一种常见的用法值得我们参考。请看标准库代码包fmt

func(s *ss)Token(skipSpace bool,f func(rune)bool)(tok []byte,err error){

defer func(){

ife:=recover();el=nil{

if se,ok:=e.(scanError);ok{

err=se.err

}else{

panic(e)

}

}

}()

//省略部分代码

}

golang panic 和 recover 什么时候用?

在Token函数包含的延迟函数中,当运行时恐慌携带值的类型是fmt.scanError时,

这个值就会被赋值给代表结果值的变量err,否则运行时恐慌就会被重新引发。如果这个

重新引发的运行时恐慌传递到了调用栈的最顶层,那么标准输出上就会打印出类似这样

的内容:

panic:

panic:

goroutine 1[running]:

main.func·001()

goroutine 2[runnable]:

exit status 2

这里展现的惯用法有两个,如下。

可以把运行时恐慌的携带值转换为error类型值,并当作常规结果返回给调用方。

这样既阻止了恐慌的扩散,又传递了引起恐慌的原因。

检查运行时恐慌携带值的类型,并根据类型做不同的后续动作,这样可以精确地

控制程序的错误处理行为。


分享到:


相關文章: