原文:https://medium.com/a-journey-with-go/go-how-does-a-program-recover-fbbbf27cc31e

​ 当程序不能正确处理错误时, 会触发 Go 的 panic,比如无效的内存访问。如果错误时意外发生的,并且没有别的方法来处理它,开发人员也可以触发 panic。理解恢复或终止的流程有助于理解程序 panic 的后果。

多个函数帧

关于 panic 和 recover 有很多典型的例子和文档,包括 Go 博客中的 "Defer, Panic, and Recover"。让我们关注一个不同的例子,其中 panic 涉及到多个 defer 函数。下面是例子:

func main() {
defer println("defer 1") level1()
} func level1() {
defer println("defer func 3")
defer func() {
if err := recover(); err != nil {
println("recovering in progress...")
}
}()
defer println("defer 2")
level2()
} func level2() {
defer println("defer func 4")
panic("foo")
}

这个程序由三个函数组成, 它们在链中被调用。一旦代码在最后一层 panic,Go将在主函数中运行这些 defer 函数:

在该阶段的中运行的代码没有恢复 panic, 然后 Go 构建父函数并且调用每个函数里的 defer

提醒一下,defer 函数是按照 后进先出(LIFO)的顺序执行的。关于 defer 函数内部信息,我建议你阅读 Go: How Does defer Statement Work?

由于一个其中一个函数可以恢复 panic,所以 Go 需要一种方法来跟踪它并恢复程序的执行。为此,每个 goroutine 都嵌入了一个特殊的属性,指向了代表 panic 的对象

当 panic 发生时,在运行 defer 函数之前创建该对象。然后,恢复 panic 的函数实际上只是返回该对象的信息,并将 panic 标记为已恢复

一旦发现 panic 已经恢复,Go需要继续当前的工作。然而,由于运行时是在 defer 中, 它不知道从哪里恢复,因此,当 panic 被标记为已恢复时,Go保存当前函数的程序计数器和堆栈指针,以便在发生 panic 之后恢复。

我们也可以用 objdump 检查程序计数器代表什么

(e.g. objdump -D my-binary | grep 105acef)

此指令指向函数调用 runtime.deferreturn, 这是由编译器插入到每个函数末尾的指令,该指令运行 deferred 函数。在前面的例子中,它们中的大多数在恢复之前已经运行,因此在返回到调用者之前只有还没被调用的。

Wait Group

理解这个工作流向我们展示了defer函数的重要性,以及如何发挥作用,例如,在处理一组 goroutine 时,在 defer 函数中延迟对 WaitGroup 对象的调用可以防止死锁,这里有一个例子:

func main() {
var wg sync.WaitGroup
wg.Add(1) go func() {
defer func() {
if err := recover(); err != nil {
println(err.(string))
}
}()
p()
wg.Done()
}()
wg.Wait()
} func p() {
panic("foo")
}

这个程序会导致死锁,因为 wg.Done 永远不会被调用。将 wg.Done 移动到一个 defer 函数可以确保被调用,程序可以继续执行

Goexit

值得注意的是函数 runtime.Goexit 使用完全相同的工作流。它实际上创建了一个带有特殊标志的 panic 对象,以区别真正的 panic。此标志允许运行时跳过恢复并正确退出,而不是停止程序的执行。

【译】Go:程序如何恢复?的更多相关文章

  1. [译]好程序员的五声“呐喊”

    通常编程情况下,会导致软件项目变坏的一些列反应 原文:The five shouts of good programmers 在任何一天,在这个世界上都有软件项目正在失败,这很常见.常见到当软件产品按 ...

  2. python学习笔记:"爬虫+有道词典"实现一个简单的英译汉程序

    1.有道的翻译 网页:www.youdao.com Fig1 Fig2 Fig3 Fig4 再次点击"自动翻译"->选中'Network'->选中'第一项',如下: F ...

  3. eclipse代码恢复(开发程序代码恢复)

    如果误操作,让本地代码丢失了不用怕,Eclipse local history可以恢复. 误删除文件后,直接ctrl+z可以恢复. 拉去代码覆盖了本地,也可以一个一个或者整体进行恢复:http://b ...

  4. [译]Java 程序员应该了解的 10 个面向对象设计原则

    面向对象设计原则是OOPS(Object-Oriented Programming System,面向对象的程序设计系统)编程的核心,但大多数Java程序员追逐像Singleton.Decorator ...

  5. Ionic1 环境破坏后程序重新恢复过程

    ionic platform remove android ionic platform add android cordova plugin add cordova-plugin-network-i ...

  6. Android清单文件详解(三)----应用程序的根节点<application>

    <application>节点是AndroidManifest.xml文件中必须持有的一个节点,它包含在<manifest>节点下.通过<application>节 ...

  7. 如何定位Release 版本中程序崩溃的位置 ---利用map文件 拦截windows崩溃函数

    1       案例描述 作为Windows程序员,平时最担心见到的事情可能就是程序发生了崩溃(异常),这时Windows会提示该程序执行了非法操作,即将关闭.请与您的供应商联系.呵呵,这句微软的“名 ...

  8. 小白学phoneGap《构建跨平台APP:phoneGap移动应用实战》连载四(使用程序载入事件)

    在了解了PhoneGap中都有哪些事件之后,本节将開始对这些事件的使用方法进行具体地介绍.本节要介绍的是程序载入事件,也就是deviceready.pause和resume这3个事件. [范例4-2 ...

  9. java程序错误类型及异常处理

    一.程序的错误类型 在程序设计中,无论规模是大是小,错误总是难免的.程序的设计很少有能够一次完成,没有错误的(不是指HelloWorld这样的程序,而是要实现一定的功能,具备一定实用价值的程序),在编 ...

随机推荐

  1. python基础之python牛逼的设计模式

    七大设计原则: 1.单一职责原则[SINGLE RESPONSIBILITY PRINCIPLE]:一个类负责一项职责. 2.里氏替换原则[LISKOV SUBSTITUTION PRINCIPLE] ...

  2. python 中的nonlocal

    python 中nonloal 关键字用来在函数或其他作用域中使用外层变量(非全局),也可使用global需要在函数外部

  3. 在Spring Bean实例过程中,如何使用反射和递归处理的Bean属性填充?

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! <Spring 手撸专栏>目录 [x] 第 1 章:开篇介绍,我要带你撸 Spri ...

  4. NCF 如何通过WebApi实现前后端分离

    简介 昨天参加了<Best Of Microsoft Build>上海专场 NCF 模块化 WebApi 系统开发 + 容器化部署实战 的小伙伴们,昨天有些没有记住的,今天我在这里给大家把 ...

  5. 让ThreadPoolExecutor的workQueue占满时自动阻塞submit()方法

    public class BlockingSubmitExecutor { private ExecutorService executor = new ThreadPoolExecutor(2, 2 ...

  6. python异步编程之asyncio

    python异步编程之asyncio   前言:python由于GIL(全局锁)的存在,不能发挥多核的优势,其性能一直饱受诟病.然而在IO密集型的网络编程里,异步处理比同步处理能提升成百上千倍的效率, ...

  7. GO学习-(27) Go语言操作NSQ

    Go语言操作NSQ NSQ是目前比较流行的一个分布式的消息队列,本文主要介绍了NSQ及Go语言如何操作NSQ. NSQ NSQ介绍 NSQ是Go语言编写的一个开源的实时分布式内存消息队列,其性能十分优 ...

  8. Raft: 一点阅读笔记

    前言 如果想要对Raft算法的了解更深入一点的话,仅仅做6.824的Lab和读<In Search of an Understandable Consensus Algorithm>这篇论 ...

  9. Manjaro Linux安装singularity-container

    技术背景 容器化技术在各种生产领域已经得到了广泛的应用,这得益于容器的轻量化(相比于虚拟机而言),安全性(隔离弱于虚拟机,但是权限控制得当的情况下也可以认为是安全隔离的)以及系统级虚拟化带来的高可用性 ...

  10. JavaScript 中数组 sort() 方法的基本使用

    在日常的代码开发中,关于数组排序的操作可不少,JavaScript 中可以调用 sort 方法对数组进行快速排序. 今天,就数组的 sort 方法来学习一下,避免日后踩坑的悲惨遭遇. 概念 sort ...