【译】Go:程序如何恢复?
原文: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:程序如何恢复?的更多相关文章
- [译]好程序员的五声“呐喊”
通常编程情况下,会导致软件项目变坏的一些列反应 原文:The five shouts of good programmers 在任何一天,在这个世界上都有软件项目正在失败,这很常见.常见到当软件产品按 ...
- python学习笔记:"爬虫+有道词典"实现一个简单的英译汉程序
1.有道的翻译 网页:www.youdao.com Fig1 Fig2 Fig3 Fig4 再次点击"自动翻译"->选中'Network'->选中'第一项',如下: F ...
- eclipse代码恢复(开发程序代码恢复)
如果误操作,让本地代码丢失了不用怕,Eclipse local history可以恢复. 误删除文件后,直接ctrl+z可以恢复. 拉去代码覆盖了本地,也可以一个一个或者整体进行恢复:http://b ...
- [译]Java 程序员应该了解的 10 个面向对象设计原则
面向对象设计原则是OOPS(Object-Oriented Programming System,面向对象的程序设计系统)编程的核心,但大多数Java程序员追逐像Singleton.Decorator ...
- Ionic1 环境破坏后程序重新恢复过程
ionic platform remove android ionic platform add android cordova plugin add cordova-plugin-network-i ...
- Android清单文件详解(三)----应用程序的根节点<application>
<application>节点是AndroidManifest.xml文件中必须持有的一个节点,它包含在<manifest>节点下.通过<application>节 ...
- 如何定位Release 版本中程序崩溃的位置 ---利用map文件 拦截windows崩溃函数
1 案例描述 作为Windows程序员,平时最担心见到的事情可能就是程序发生了崩溃(异常),这时Windows会提示该程序执行了非法操作,即将关闭.请与您的供应商联系.呵呵,这句微软的“名 ...
- 小白学phoneGap《构建跨平台APP:phoneGap移动应用实战》连载四(使用程序载入事件)
在了解了PhoneGap中都有哪些事件之后,本节将開始对这些事件的使用方法进行具体地介绍.本节要介绍的是程序载入事件,也就是deviceready.pause和resume这3个事件. [范例4-2 ...
- java程序错误类型及异常处理
一.程序的错误类型 在程序设计中,无论规模是大是小,错误总是难免的.程序的设计很少有能够一次完成,没有错误的(不是指HelloWorld这样的程序,而是要实现一定的功能,具备一定实用价值的程序),在编 ...
随机推荐
- mysql默认值
1.创建表时添加默认值 语法: <字段名><类型><默认值> 实例: MySQL [wordpress]> create table ly_content( ...
- http协议工作原理及工作流程
什么是url ? url = 协议 + 域名 + 资源路径 比如: https://www.baidu.com/index.html http : 超文本传输协议 https: 安全套接字协议 HTT ...
- sersync 实时同步网站数据
sersync同步实战 sersync是基于inotify和srync的二次开发,代码托管在github上面在rsync的客户端上安装,下载地址: https://raw.githubusercont ...
- linux进阶之SVN集中式版本控制系统篇
代码上线--版本控制系统 SVN 集中式版本控制系统 git 分布式版本控制系统 SVN是subversion的缩写,即版本控制系统,是一个开放 ...
- Ascend Pytorch算子适配层开发
Ascend Pytorch算子适配层开发 适配方法 找到和PyTorch算子功能对应的NPU TBE算子,根据算子功能计算出输出Tensor的size,再根据TBE算子原型构造对应的input/ou ...
- 使用Runtime执行推理(C++)
使用Runtime执行推理(C++) 概述 通过MindSpore Lite模型转换后,需在Runtime中完成模型的推理执行流程.本教程介绍如何使用C++接口编写推理代码. Runtime总体使用流 ...
- Yolov3&Yolov4网络结构与源码分析
Yolov3&Yolov4网络结构与源码分析 从2018年Yolov3年提出的两年后,在原作者声名放弃更新Yolo算法后,俄罗斯的Alexey大神扛起了Yolov4的大旗. 文章目录 1. 论 ...
- C++/VS基础篇
------------恢复内容开始------------ VS: 1.项目配置 2.IDE设置 错误列表是输出窗口的大概,根据error语法整理出,不准确. C++: 1.C++特点 优点 可直接 ...
- Django(56)Mixins工具集的使用
前言 mixins翻译成中文是混入,组件的意思.在DRF中,针对获取列表,检索,创建等操作,都有相应的mixin,一般我们自定义创建的类视图都会继承自GenericAPIView和Mixins一起使用 ...
- mybatis之Param注解
一.作用 使用@Param注解表示给参数命名,名称就是括号中的内容.给参数命名,然后在映射文件中就能根据名称获取参数值了.在mybatis中我们常常要使用到多个参数,但是在xml中的parameter ...