zoukankan      html  css  js  c++  java
  • [Go] 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 error!"))
    }

    当调用 innerFunc 函数中的 panic 函数后,innerFunc 的执行会被停止。紧接着,流程控制权会交回调用方 outerFunc 函数。然后,outerFunc 函数的执行也将被停止。运行时恐慌就这样沿着调用栈反方向进行传播,直至到达当前 goroutine 的调用栈的最顶层。一旦达到顶层,就意味着该 goroutine 调用栈中所有函数的执行都已经被停止了,程序已经崩溃。

    当然,运行时恐慌并不都是通过调用 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() {
        if p := recover(); p != nil {
            fmt.Printf("Recovered panic: %s
    ", p)
        }
    }()

    把此类代码放在函数体的开始处,这样可以有效防止该函数及其下层调用中的代码引发运行时恐慌。一旦发现 recover 函数的调用结果是 非 nil,就应该采取相应的措施。

    值得一提的是,Go 标准库中有一种常见的用法值得我们参考。请看标准库代码包 fmt 中的 Token 函数的部分声明:

    func (s *ss) Token(skipSpace bool, f func(rune) bool) (tok []byte, err error) {
    	defer func() {
    		if e := recover(); e != nil {
    			if se, ok := e.(scanError); ok {
    				err = se.err
    			} else {
    				panic(e)
    			}
    		}
    	}()
    	// 省略部分代码
    }

    在 Token 函数包含的 延迟函数 中,当运行时恐慌携带值的类型是 fmt.scanError 时,这个值就会被赋值给代表结果值的变量 err,否则运行时恐慌就会被重新引发。如果这个重新引发的运行时恐慌传递到了调用栈的最顶层,那么标准输出上就会打印类似这样的内容:

    panic: <运行时恐慌被首次引发时携带的值的字符串形式> [recovered]
        panic: <运行时恐慌被重新引发时携带的值的字符串形式>
    goroutine 1 [running]:
    main.func.001()
    <调用栈信息>
    
    goroutine 2 [runnable:]
    exit status 2    

    这里展现的惯用法有 2 个,如下:

    1. 可以把运行时恐慌的携带值转换为 error 类型值,并当作常规结果返回给调用方。这样既阻止了恐慌的扩散,又传递了引起恐慌的原因。
    2. 检查运行时恐慌携带值的类型,并根据类型做不同的后续动作,这样可以精确地控制程序的错误处理行为。

    摘自:《Go 并发编程实战(第二版) . 郝林》

  • 相关阅读:
    Mysql蠕虫复制
    Mysql中如何开启慢查询功能?
    线程的状态以及状态切换
    Java的Unsafe类
    Spring 获取jar内外文件的方式
    RocketMQ学习
    volatile的理解
    快速排序
    JVM的发展史
    nginx安装配置
  • 原文地址:https://www.cnblogs.com/52php/p/6714742.html
Copyright © 2011-2022 走看看