## 5.10. Recover捕获异常

通常来说,不应该对panic异常做任何处理,但有时,也许我们可以从异常中恢复,至少我们可以在程序崩溃前,做一些操作。举个例子,当web服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭;如果不做任何处理,会使得客户端一直处于等待状态。如果web服务器还在开发阶段,服务器甚至可以将异常信息反馈到客户端,帮助调试。

如果在deferred函数中调用了内置函数recover,并且定义该defer语句的函数发生了panic异常,recover会使程序从panic中恢复,并返回panic value。导致panic异常的函数不会继续运行,但能正常返回。在未发生panic时调用recover,recover会返回nil。

让我们以语言解析器为例,说明recover的使用场景。考虑到语言解析器的复杂性,即使某个语言解析器目前工作正常,也无法肯定它没有漏洞。因此,当某个异常出现时,我们不会选择让解析器崩溃,而是会将panic异常当作普通的解析错误,并附加额外信息提醒用户报告此错误。

```Go

func Parse(input string) (s *Syntax, err error) {

defer func() {

if p := recover(); p != nil {

err = fmt.Errorf("internal error: %v", p)

}

}()

// ...parser...

}

```

deferred函数帮助Parse从panic中恢复。在deferred函数内部,panic value被附加到错误信息中;并用err变量接收错误信息,返回给调用者。我们也可以通过调用runtime.Stack往错误信息中添加完整的堆栈调用信息。

不加区分的恢复所有的panic异常,不是可取的做法;因为在panic之后,无法保证包级变量的状态仍然和我们预期一致。比如,对数据结构的一次重要更新没有被完整完成、文件或者网络连接没有被关闭、获得的锁没有被释放。此外,如果写日志时产生的panic被不加区分的恢复,可能会导致漏洞被忽略。

虽然把对panic的处理都集中在一个包下,有助于简化对复杂和不可以预料问题的处理,但作为被广泛遵守的规范,你不应该试图去恢复其他包引起的panic。公有的API应该将函数的运行失败作为error返回,而不是panic。同样的,你也不应该恢复一个由他人开发的函数引起的panic,比如说调用者传入的回调函数,因为你无法确保这样做是安全的。

有时我们很难完全遵循规范,举个例子,net/http包中提供了一个web服务器,将收到的请求分发给用户提供的处理函数。很显然,我们不能因为某个处理函数引发的panic异常,杀掉整个进程;web服务器遇到处理函数导致的panic时会调用recover,输出堆栈信息,继续运行。这样的做法在实践中很便捷,但也会引起资源泄漏,或是因为recover操作,导致其他问题。

基于以上原因,安全的做法是有选择性的recover。换句话说,只恢复应该被恢复的panic异常,此外,这些异常所占的比例应该尽可能的低。为了标识某个panic是否应该被恢复,我们可以将panic value设置成特殊类型。在recover时对panic value进行检查,如果发现panic value是特殊类型,就将这个panic作为errror处理,如果不是,则按照正常的panic进行处理(在下面的例子中,我们会看到这种方式)。

下面的例子是title函数的变形,如果HTML页面包含多个`

`,该函数会给调用者返回一个错误(error)。在soleTitle内部处理时,如果检测到有多个``,会调用panic,阻止函数继续递归,并将特殊类型bailout作为panic的参数。

```Go

// soleTitle returns the text of the first non-empty title element

// in doc, and an error if there was not exactly one.

func soleTitle(doc *html.Node) (title string, err error) {

type bailout struct{}

defer func() {

switch p := recover(); p {

case nil: // no panic

case bailout{}: // "expected" panic

err = fmt.Errorf("multiple title elements")

default:

panic(p) // unexpected panic; carry on panicking

}

}()

// Bail out of recursion if we find more than one nonempty title.

forEachNode(doc, func(n *html.Node) {

if n.Type == html.ElementNode && n.Data == "title" &&

n.FirstChild != nil {

if title != "" {

panic(bailout{}) // multiple titleelements

}

title = n.FirstChild.Data

}

}, nil)

if title == "" {

return "", fmt.Errorf("no title element")

}

return title, nil

}

```

在上例中,deferred函数调用recover,并检查panic value。当panic value是bailout{}类型时,deferred函数生成一个error返回给调用者。当panic value是其他non-nil值时,表示发生了未知的panic异常,deferred函数将调用panic函数并将当前的panic value作为参数传入;此时,等同于recover没有做任何操作。(请注意:在例子中,对可预期的错误采用了panic,这违反了之前的建议,我们在此只是想向读者演示这种机制。)

有些情况下,我们无法恢复。某些致命错误会导致Go在运行时终止程序,如内存不足。

**练习5.19:** 使用panic和recover编写一个不包含return语句但能返回一个非零值的函数。

recover 没有捕获异常_Recover捕获异常相关推荐

  1. 捕获异常_Recover捕获异常

    " 本文来源于<The Go Programming Language>" 5.10. Recover捕获异常 通常来说,不应该对panic异常做任何处理,但有时,也许 ...

  2. pyqt5 捕获异常确保程序不退出_Python异常处理详解(基础篇十一)

    一 异常 1 什么是异常? 示例: 说明: 打开一个不存在的文件123.txt,当找不到123.txt 文件时,就会抛出给我们一个IOError类型的错误,No such file or direct ...

  3. go panic recover 思考

    panic 作为 Go 异常的一种,类比其它语言的 Exception. 常见的 panic 有数组下标越界,或者除数为0,这类 panic 可以通过 recover 进行捕获.但要特别注意,程序发生 ...

  4. Golang中panic与recover的实现原理

    今天我们讲讲golang中panic异常,以及recover对异常的捕获,由于panic.recover.defer之间非常亲密,所以今天就放在一起讲解,这里会涉及到一些defer的知识,有兴趣可以看 ...

  5. Go 语言从入门到实战

    <Go 语言从入门到实战> 的学习笔记,欢迎阅读斧正.感觉该专栏整体来说对有些后端编程经验的来说比无后端编程经验的人更友好. 数据类型 运算符 算数运算符 比较运算符 用 == 比较数组 ...

  6. defer 的使用与原理

    1. 前言 defer 是 go 语言的关键字,被 defer 修饰的 func,将会在函数返回之前执行. defer 具有以下特点 延迟执行 参数预计算 同一 goroutine 中多个 defer ...

  7. 使用dlv调试golang程序

    1.编译选项 go build -gcflags=all="-N -l" ## 必须这样编译,才能用gdb打印出变量,第二个是小写的L,不是大写的i 需要加编译选项,类似gcc中的 ...

  8. java代码走查常见错误_FindBugs常见错误描述和解决方法

    (一)[DLS_DEAD_LOCAL_STORE] 描述: Dead store to 未使用的局部变量 解决方法:局部变量定义后未使用:实例化对象后又重新对该对象赋值 (二) [ST_WRITE_T ...

  9. 半吊子菜鸟学Web开发 -- PHP学习 4 --异常

    PHP异常处理 1 抛出一个异常 与Python的try except类似,PHP用try catch来捕获异常 基本语法 try{//可能出现错误或异常的代码//catch表示捕获,Exceptio ...

  10. Java的知识点19——异常机制Exception

    异常(Exception)的概念 异常指程序运行过程中出现的非正常现象,例如用户输入错误.除数为零.需要处理的文件不存在.数组下标越界等. Java是采用面向对象的方式来处理异常的.处理过程: 1.  ...

最新文章

  1. Scala --- 第三章 数组相关操作
  2. SharePoint【Query Options系列】-- Query Options的一些用法 01. 展开用户列信息
  3. Android之四大组件概念讲解
  4. java电话号码输入_使用可选字母前缀屏蔽输入到电话号码格式
  5. oc 中随机数的用法(arc4random() 、random()、CCRANDOM_0_1()
  6. Java多线程两种实现方式的对比
  7. apache开源项目--ZooKeeper
  8. 使用Servlet 3.0,Redis / Jedis和CDI的简单CRUD –第2部分
  9. 【计算机视觉】论文笔记:Ten years of pedestrian detection, what have we learned?
  10. oracle如何规则显示,Oracle语句优化规则汇总
  11. HALCON 21.11:学习笔记---OPC_UA(I/O)
  12. excel文件损坏修复绝招_ps文件损坏有修复的软件!超强开挂辅助神器
  13. # 一点毕设的小感悟(词频统计+可视化分析)
  14. SQL中Date 函数
  15. 1 1 2 3 5 8 java_java for循环完成输出(1,1,2,3,5,8,13,21,34...)求出第10个数是多少?...
  16. Linux为什么不怕病毒
  17. React 面试题 回答
  18. .net cf wince 贝兹 曲线图
  19. 服务器任务栏不显示程序,win10系统下任务栏不显示程序窗口预览图怎么处理
  20. SOM网络1:原理讲解

热门文章

  1. 面试题之细胞分裂问题
  2. java+s2sh+mysql报刊订阅系统系统
  3. WTS_ERAL_年假生成规则
  4. 自制Beamer主题
  5. matlab抗混叠滤波器,抗混叠滤波器讲解.doc
  6. 动手学深度学习 - 11.1. 数学符号 (notation)
  7. android icloud照片恢复软件,iCloud照片怎么恢复到手机 iCloud照片恢复到手机方法【详细步骤】...
  8. 平台的边际成本与商家的边际成本
  9. 搭建 IPv6 Web服务器
  10. Fortran系列(一):KIND的用法