go 语言中的三种报错机制

  • 第一种,也就是常说的 error 类型错误,不需要 recover 拯救,处理方式自己决定

  • 第二种,panic 和 recover 是紧密集合的,有点类似 try catch,recover 能捕获到 panic

  • 第三种,一些 Go 语言系统级别的错误,比如发生死锁,数据竞争,这种错误程序会立刻报错,无法 recover

recove 的作用

go 语言中,错误一般会由 error 触发,但是如果比较严重的错误(通常是没有恰当处理的 error,也可是手动触发) 会造成 panic 。 一旦主程序 panic ,会导致整个程序挂掉。如果这个错误不是那么严重,我们希望程序可以继续往下执行,而不是整个程序挂掉。

  1. recover 函数,对 panic 错误进行拦截,避免上传给主函数,进而避免整个程序挂掉。
  2. 可以在程序崩溃前,做一些操作,举个例子,当 web 服务器遇到不可预料的严重问题时,在崩溃前应该将所有的连接关闭,如果不做任何处理,会使得客户端一直处于等待状态。

下面例子

如果给 out 函数传入两个相同的形参,就会引发 panic

如果没有 recover 拦截,fmt.Print 这行是执行不到的。

func main() {
Out(1, 1)
fmt.Println("*******此行函数依然能继续执行******")
}
func Out(numb1, numb2 int) bool {
defer func() {
if r := recover(); r != nil { # 在此处对panic进行拦截,不会将错误继续上报上去。
fmt.Println("异常已扑捉,避免继续往上层传递")
}
}()
if numb1 == numb2 {
panic("两个数不能相等")
}
return numb1 > numb2
}

知识点

  1. recover 仅在延迟函数 defer 中有效
  2. 因为 Go 语言没有异常系统,其使用 panic 触发宕机类似于其他语言的抛出异常,recover 的宕机恢复机制就对应其他语言中的 try/catch 机制。
  3. 谨记一点 recover 只能恢复本协程的 panic

注意:即使是子协程内引发的 panic 依然会导致主程序的挂掉,如下面的例子

func main() {
go OutOne()
go OutTwo()
time.Sleep(time.Minute)
} func OutOne() {
panic("错误")
}
func OutTwo() {
for i := 0; i < 10; i++ {
time.Sleep(time.Second)
fmt.Print("****此处继续执行***")
}
}

进阶

那么上面情况该怎么解决?

其实就是在发生 panic 的协程里面,用 recover 进行拦截。让它传不到主函数。

func main() {
go OutOne()
go OutTwo()
time.Sleep(time.Minute)
} func OutOne() {
defer func() {
if r := recover(); r != nil {
fmt.Println("OutOne 的报错已经别扑捉", r)
}
}()
panic("错误")
}
func OutTwo() {
for i := 0; i < 10; i++ {
time.Sleep(time.Second)
fmt.Println("****此处继续执行***")
}
}

当panic、defer和子函数混合使用时的执行顺序

func f() {
fmt.Println("打开文件b")
defer func() {
fmt.Println("关闭文件b")
}()
panic("文件b读取异常")
} func main() {
fmt.Println("打开文件a")
defer func() { fmt.Println("关闭文件a") }()
f()
fmt.Println("文件正常执行")
} # 输出
打开文件a
打开文件b
关闭文件b
关闭文件a
panic: 文件b读取异常
  • 记住这个例题,当 go 语言发生panic时,会首先执行已经“进栈”的 defer函数,最后然后在报出panic的错误。传给上层的函数。
  • 当子函数发生 panic的时候,函数依然是会先执行已经“进栈”的 defer函数,而爆出 panic

defer

go的代码中,defer 有点类似于压栈操作,只有被执行到了,才会入栈(延迟执行),但是实际上,defer函数是编译过程中,通过调整代码顺序实现

错误日志如何查看

package main

import "log"

func main() {
err := funcA()
if err != nil {
log.Println("日志输出错误")
}
} func funcA() error {
panic("错误")
}

输出:

  • 观察错误输出顺序,错误就是一个入栈的过程,会从底到上的打印,直到遇到 recover
  • 日志能不能输出这条错误,要看这条日志能不能走到。

参考文献

  1. https://www.zhihu.com/question/371695315/answer/1026803101

  2. http://c.biancheng.net/view/64.html

最新文章

  1. question
  2. 【wikioi】1017 乘积最大
  3. IOS开发-封装数据库sqlite3之为何选择FMDB
  4. SQL数据库基础(六)
  5. jquery点击改变图片src源码并toggle
  6. 【转】MySQL GRANT REVOKE用法
  7. ASP.NET Web - 开篇
  8. 介绍SmartUpload很好的网站
  9. expdp时遇到ORA-31693&amp;amp;ORA-02354&amp;amp;ORA-01466
  10. NSIS:获取硬盘中容量最大的分区盘符
  11. Swaps in Permutation
  12. 5个步骤创建你的第一个RESTFul 服务
  13. vue中使用stompjs实现mqtt消息推送通知
  14. 取消PHPCMS V9后台新版本升级提示信息
  15. 网卡bond技术
  16. 初尝Eclipse
  17. HTML文档结构
  18. Codeforces891C(892E)
  19. 经典的PHPer为什么被认为是草根?
  20. 【快捷键】IntelliJ IDEA For Mac 常用快捷键

热门文章

  1. Ubuntu vmware共享文件夹自动挂载
  2. AC 自动机上 DP
  3. [EULAR文摘] TNFi治疗3年对384例强柱患者脊柱放射学进展的影响
  4. LeetCode算法训练 93.复原IP地址 78.子集 90.子集II
  5. Java处理正则匹配卡死(正则回溯问题)
  6. LeetCode-1994 好子集的数目
  7. rotate matrix
  8. 死磕rmi之 RegistryImpl
  9. div溢出横向滚动
  10. 让CSS flex布局最后一行列表左对齐的N种方法