热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Go语言Defer,Panic,andRecover

不忘初心,砥砺前行 

不忘初心,砥砺前行 

作者 | 陌无崖

转载请联系授权 

Defer,Panic,and Recover

Andrew Gerrand
4 August 2010

Go拥有一般的控制流程机制,像if、for、switch、goto。除此之外go也拥有一个单独的goroutine机制运行go语句。这里我想讨论一些不太常见的语法:defer,panic,and recover

defer语句将函数调用推送到列表上,这个保存的列表会在周围的函数执行之后才开始执行,defer通常用在简化执行各种清理功能的函数。

例如,让我们看一个打开两个文件并将一个文件的内容复制到另一个文件的函数:

func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
    written, err = io.Copy(dst, src)
    dst.Close()
    src.Close()
    return
}

上面的代码是可行的,但是存在一个bug,如果运行中调用os.Create()失败,这个函数会返回一个没有关闭的文件资源。在第二个return语句调用之前放置一个src.Close()可以轻松的解决这个问题。但是通过引入defer语句,我们可以确保我们的文件总是关闭的:

func CopyFile(dstName, srcName string) (written int64, err error) {
    src, err := os.Open(srcName)
    if err != nil {
        return
    }
    defer src.Close()
    dst, err := os.Create(dstName)
    if err != nil {
        return
    }
    defer dst.Close()
    return io.Copy(dst, src)
}

Defer语句使我们可以考虑正确关闭每一个打开的文件,从而保证无论函数的返回语句的数量如何,文件都会被关闭。

Defer语句的行为是直观的和可预测的.这有三个简单的规则:

1. 当对defer语句进行评价(使用)时,将对延迟函数的参数进行求值

在这个例子中,当Println()函数被延迟执行的时候,i运算式被使用,延迟调用将在函数返回后打印“0”。

func a() {
    i := 0
    defer fmt.Println(i)
    i
    return
}

2. 当周围的函数返回后,defer函数按照后进先出的顺序进行调用。

这个函数输出 "3210"

func b() {
    for i := 0; i < 4; i  {
        defer fmt.Print(i)
    }
}

3. defer函数可以读取和分配给返回函数的命名返回值

这个例子中,defer函数在周围的函数执行后递增返回i,因此这个函数返回2

func c() (i int) {
    defer func() { i  }()
    return 1
}

这对于修改错误返回值很方便,我们将很快看到这样的一个例子。

Panic是一个内置的函数,它可以停止常规控制流并开始panic,F函数调用了panic时,F的执行会被停止,F中的任何defer函数正常执行,然后F返回给它的调用者,对于调用者,F的行为是一个panic的调用,该过程将会继续向上进行堆栈直到返回当前的goroutine中的所有函数都返回,此时程序崩溃,panic可以直接通过引用panic来引发panic,它们也可以在程序运行错误的时候导致,比如越界数组的访问。

Recover是一个内置函数,它可以重新获取正在panic线程的控制。恢复仅仅在defer函数内部有用。当正常执行期间,recover会返回nil并且没有其它的效果。如果当前的goroutine正在panic,recover将会给panic一个值使其恢复正常执行。

这有一个panic和recover的例子,演示了这种机制:

package main
import "fmt"
func main() {
    f()
    fmt.Println("Returned normally from f.")
}
func f() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
    fmt.Println("Calling g.")
    g(0)
    fmt.Println("Returned normally from g.")
}
func g(i int) {
    if i > 3 {
        fmt.Println("Panicking!")
        panic(fmt.Sprintf("%v", i))
    }
    defer fmt.Println("Defer in g", i)
    fmt.Println("Printing in g", i)
    g(i   1)
}

函数g接收了int i,如果i大于3,则发生panic,否则它将使用参数i 1进行调用自身,函数f defer会被调用reecover并打印恢复值(如果非零)的函数。再继续阅读之前,请尝试描绘出该程序的输出内容。

这个程序会输出

Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
Recovered in f 4
Returned normally from f.

如果从f中移除了这个defer函数,这个panic将不会被恢复并且将直接到达goroutine调用堆栈的帝国不,从而终止了程序,这个修改后的程序将会输出:

Calling g.
Printing in g 0
Printing in g 1
Printing in g 2
Printing in g 3
Panicking!
Defer in g 3
Defer in g 2
Defer in g 1
Defer in g 0
panic: 4
panic PC=0x2a9cd8
[stack trace omitted]

有关panic和recover的实际示例,请参见Go标准库中的 json package它使用了一组递归函数对接口进行编码,如果遍历该值的时候发生了错误,则会调用panic将堆栈展开到顶级函数调用,该调用从panic中恢复并且返回适当的错误值(请参阅encode.go中encodeState类型的error和marshal方法)

在Go库中的约定甚至是当一个内部包使用了panic,它外部的API仍然会显示的显示出错误值。

defer的其他用法(在文件之外。前面给出的关闭示例)包括释放互斥量

mu.Lock()
defer mu.Unlock()

打印尾部内容

printHeader()
defer printFooter()

总而言之,defer语句(带有或不带有panic和recovery)提供了一种异常强大的控制流机制。它可以用来建模由其他编程语言中的专用结构实现的许多功能。试试看。

本文为Golang官方博客部分文章的外文翻译,官方案例更加有料哦

查看完整源码可以点击阅读原文进入github仓库,如果喜欢,感谢你为我点一个星星^_^

END

今日推荐阅读

RabbitMQ系列笔记广播模式和路由模式 
RabbitMQ系列笔记入门篇

RabbitMQ系列笔记work模式

RabbitMQ系列笔记work模式

protoc语法详解及结合grpc定义服务

Golang中Model的使用

基于Nginx和Consul构建高可用及自动发现的Docker服务架构

▼关注我,一起成长

    主要分享 学习心得、笔记、随笔▼

    


推荐阅读
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了Perl的测试框架Test::Base,它是一个数据驱动的测试框架,可以自动进行单元测试,省去手工编写测试程序的麻烦。与Test::More完全兼容,使用方法简单。以plural函数为例,展示了Test::Base的使用方法。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • (三)多表代码生成的实现方法
    本文介绍了一种实现多表代码生成的方法,使用了java代码和org.jeecg框架中的相关类和接口。通过设置主表配置,可以生成父子表的数据模型。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • Commit1ced2a7433ea8937a1b260ea65d708f32ca7c95eintroduceda+Clonetraitboundtom ... [详细]
author-avatar
鹰击长空1943
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有