返回顶部
首页 > 资讯 > 精选 >Go的defer、panic和recover怎么用
  • 570
分享到

Go的defer、panic和recover怎么用

2023-06-30 04:06:02 570人浏览 八月长安
摘要

这篇文章主要介绍“Go的defer、panic和recover怎么用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go的defer、panic和recover怎么用”文章能帮助大家解决问题。defe

这篇文章主要介绍“Go的defer、panic和recover怎么用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go的defer、panic和recover怎么用”文章能帮助大家解决问题。

defer关键字

defer关键字可以让函数或语句延迟到函数语句块的最结尾时,即即将退出函数时执行,即便函数中途报错结束、即便已经panic()、即便函数已经return了,也都会执行defer所推迟的对象。

其实defer的本质是,当在某个函数中使用了defer关键字,则创建一个独立的defer栈帧,并将该defer语句压入栈中,同时将其使用的相关变量也拷贝到该栈帧中(显然是按值拷贝的)。因为栈是LIFO方式,所以先压栈的后执行。因为是独立的栈帧,所以即使调用者函数已经返回或报错,也一样能在它们之后进入defer栈帧去执行。

例如:

func main() {    a()}func a() {    println("in a")    defer b()              // 将b()压入defer栈中    println("leaving a")    //到了这里才会执行b()}func b() {    println("in b")    println("leaving b")}

上面将输出:

in aleaving ain bleaving b

即便是函数已经报错,或函数已经return返回,defer的对象也会在函数退出前的最后一刻执行。

func a() TYPE{    ...CODE...        defer b()        ...CODE...        // 函数执行出了错误        return args    // 函数b()都会在这里执行}

但注意,由于Go的作用域采用的是词法作用域,defer的定义位置决定了它推迟对象能看见的变量值,而不是推迟对象被调用时所能看见的值。

例如:

package mainvar x = 10func main() {    a()}func a() {println("start a:",x)   // 输出10x = 20defer b(x)       // 压栈,并按值拷贝20到栈中x = 30    println("leaving a:",x)  // 输出30    // 调用defer延迟的对象b(),输出20}func b(x int) {    println("start b:",x)}

比较下面的defer:

package mainvar x = 10func main() {a()}func a() int {println("start a:", x) // 输出10x = 20defer func() {      // 压栈,但并未传值,所以内部引用xprintln("in defer:", x)  // 输出30}()x = 30println("leaving a:", x) // 输出30return x}

上面defer推迟的匿名函数输出的值是30,它看见的不应该是20吗?先再改成下面的:

package mainvar x = 10func main() {a()}func a() int {println("start a:", x) // 输出10x = 20defer func(x int) {println("in defer:", x)  // 输出20}(x)x = 30println("leaving a:", x) // 输出30return x}

这个defer推迟的对象中看见的却是20,这和第一种defer b(x)是相同的。

原因在于defer推迟的如果是函数,它直接就在它的定义位置处评估好参数、变量。该拷贝传值的拷贝传值,该指针相见的指针相见。所以,对于第(1)和第(3)种情况,在defer的定义位置处,就将x=20拷贝给了推迟的函数参数,所以函数内部操作的一直是x的副本。而第二种情况则是直接指向它所看见的x=20那个变量,则个变量是全局变量,当执行x=30的时候会将其值修改,到执行defer推迟的对象时,它指向的x的值已经是修改过的。

再看下面这个例子,将defer放进一个语句块中,并在这个语句块中新声明一个同名变量x:

func a() int {println("start a:", x) // 输出10x = 20{x := 40defer func() {println("in defer:", x)  // 输出40}()}x = 30println("leaving a:", x) // 输出30return x}

上面的defer定义在语句块中,它能看见的x是语句块中x=40,它的x指向的是语句块中的x。另一方面,当语句块结束时,x=40的x会消失,但由于defer的函数中仍有x指向40这个值,所以40这个值仍被defer的函数引用着,它直到defer执行完之后才会被GC回收。所以defer的函数在执行的时候,仍然会输出40。

如果语句块内有多个defer,则defer的对象以LIFO(last in first out)的方式执行,也就是说,先定义的defer后执行。

func main() {println("start...")defer println("1")defer println("2")defer println("3")defer println("4")println("end...")}

将输出:

start...end...4321

defer有什么用呢?一般用来做善后操作,例如清理垃圾、释放资源,无论是否报错都执行defer对象。另一方面,defer可以让这些善后操作的语句和开始语句放在一起,无论在可读性上还是安全性上都很有改善,毕竟写完开始语句就可以直接写defer语句,永远也不会忘记关闭、善后等操作。

例如,打开文件,关闭文件的操作写在一起:

open()defer file.Close()... 操作文件 ...

以下是defer的一些常用场景:

  • 打开关闭文件

  • 定、释放锁

  • 建立连接、释放连接

  • 作为结尾输出结尾信息

  • 清理垃圾(如临时文件)

panic()和recover()

panic()用于产生错误信息并终止当前的goroutine,一般将其看作是退出panic()所在函数以及退出调用panic()所在函数的函数。例如,G()中调用F(),F()中调用panic(),则F()退出,G()也退出。

注意,defer关键字推迟的对象是函数最后调用的,即使出现了panic也会调用defer推迟的对象。

例如,下面的代码中,main()中输出一个start main之后调用a(),它会输出start a,然后就panic了,panic()会输出panic: panic in a,然后报错,终止程序。

func main() {println("start main")a()println("end main")}func a() {println("start a")panic("panic in a")println("end a")}

执行结果如下:

start mainstart apanic: panic in agoroutine 1 [running]:main.a()        E:/learning/err.go:14 +0x63main.main()        E:/learning/err.go:8 +0x4cexit status 2

注意上面的end aend main都没有被输出。

可以使用recover()去捕获panic()并恢复执行。recover()用于捕捉panic()错误,并返回这个错误信息。但注意,即使recover()捕获到了panic(),但调用含有panic()函数的函数(即上面的G()函数)也会退出,所以如果recover()定义在G()中,则G()中调用F()函数之后的代码都不会执行(见下面的通用格式)。

以下是比较通用的panic()和recover()的格式:

func main() {    G()    // 下面的代码会执行    ...CODE IN MAIN...}func G(){    defer func (){        if str := recover(); str != nil {            fmt.Println(str)        }    }()    ...CODE IN G()...        // F()的调用必须在defer关键字之后    F()    // 该函数内下面的代码不会执行    ...CODE IN G()...}func F() {    ...CODE1...    panic("error found")    // 下面的代码不会执行    ...CODE IN F()...}

可以使用recover()去捕获panic()并恢复执行。但以下代码是错误的:

func main() {println("start main")a()println("end main")}func a() {println("start a")panic("panic in a")    // 直接放在panic后是错误的    panic_str := recover()    println(panic_str)println("end a")}

之所以错误,是因为panic()一出现就直接退出函数a()和main()了。要想recover()真正捕获panic(),需要将recover()放在defer的推迟对象中,且defer的定义必须在panic()发生之前。

例如,下面是通用格式的示例:

package mainimport "fmt"func main() {println("start main")b()println("end main")}func a() {println("start a")panic("panic in a")println("end a")}func b() {println("start b")defer func() {if str := recover(); str != nil {fmt.Println(str)}}()a()println("end b")}

以下是输出结果:

start mainstart bstart apanic in aend main

注意上面的end bend a都没有被输出,但是end main输出了。

panic()是内置的函数(在包builtin中),在log包中也有一个Panic()函数,它调用Print()输出信息后,再调用panic()。go doc log Panic一看便知:

$ go doc log Panicfunc Panic(v ...interface{})    Panic is equivalent to Print() followed by a call to panic().

关于“Go的defer、panic和recover怎么用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网精选频道,小编每天都会为大家更新不同的知识点。

--结束END--

本文标题: Go的defer、panic和recover怎么用

本文链接: https://lsjlt.com/news/327344.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Go的defer、panic和recover怎么用
    这篇文章主要介绍“Go的defer、panic和recover怎么用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go的defer、panic和recover怎么用”文章能帮助大家解决问题。defe...
    99+
    2023-06-30
  • Go基础教程系列之defer、panic和recover详解
    defer关键字 defer关键字可以让函数或语句延迟到函数语句块的最结尾时,即即将退出函数时执行,即便函数中途报错结束、即便已经panic()、即便函数已经return了,也都会执...
    99+
    2024-04-02
  • defer关键字、panic和recover的示例分析
    这篇文章给大家介绍defer关键字、panic和recover的示例分析,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。defer关键字defer关键字可以让函数或语句延迟到函数语句块的...
    99+
    2024-04-02
  • Golang异常处理之defer,panic,recover的使用详解
    目录延迟是什么延迟函数延迟⽅法延迟参数堆栈的推迟延迟的应⽤panic和recover(宕机和宕机恢复)panic和recover机制示例代码延迟是什么 defer即延迟语句,极个别的...
    99+
    2024-04-02
  • Golang异常处理之defer,panic,recover如何使用
    今天小编给大家分享一下Golang异常处理之defer,panic,recover如何使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来...
    99+
    2023-06-30
  • Go语言使用defer+recover解决panic导致程序崩溃的问题
    案例:如果我们起了一个协程,但这个协程出现了panic,但我们没有捕获这个协程,就会造成程序的崩溃,这时可以在goroutine中使用recover来捕获panic,进行处理,这样主...
    99+
    2024-04-02
  • golang函数的defer和panic
    defer 和 panic 关键字用于控制异常和后置处理:defer:将函数压入栈,在函数返回后执行,常用于释放资源。panic:抛出异常,中断程序执行,用于处理无法继续运行的严重错误。...
    99+
    2024-04-20
    defer panic golang
  • Golang异常处理中的panic和recover
    在 go 中,panic 和 recover 用于异常处理。panic 用来报告异常,recover 用来从异常中恢复。panic 会停止程序执行,抛出一个 interface{} 类型...
    99+
    2024-04-15
    recover panic golang
  • Golang 函数中的 panic 和 recover 机制
    panic 函数引发异常并终止当前函数,recover 函数捕获 panic 引发的异常。在 golang 中,panic 和 recover 函数用于处理程序中的错误和异常情况,pan...
    99+
    2024-05-24
    recover panic golang
  • Go中的panic/recover简介与实践记录
    目录简介1.特性2.panic触发流程3.recover使用要点4.使用场景一、实践1.跨线程失效2.不起作用的recover3.嵌套使用panic4.注意事项小结简介 go语言追求...
    99+
    2023-05-18
    go panic recover go panic recover使用
  • Go之panic函数和recover函数使用及捕获异常的方法是什么
    这篇文章主要介绍“Go之panic函数和recover函数使用及捕获异常的方法是什么”,在日常操作中,相信很多人在Go之panic函数和recover函数使用及捕获异常的方法是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法...
    99+
    2023-07-05
  • Golang错误捕获Panic与Recover的使用
    目录一、Golang 错误是什么?二、错误校验1.方法2.判断错误三、错误捕获1.方法2.defer 的使用总结一、Golang 错误是什么? 对于Go语言(Golang)的错误是通...
    99+
    2024-04-02
  • Golang中panic与recover的区别是什么
    这篇文章主要介绍“Golang中panic与recover的区别是什么”,在日常操作中,相信很多人在Golang中panic与recover的区别是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Golan...
    99+
    2023-07-02
  • Go错误处理之panic函数和recover函数使用及捕获异常方法
    目录前言panic函数recover函数总结前言 前面我们讲过了error类型来处理一般的错误,本文会描述使用panic函数和recover函数来处理比较极端的错误。简单来说,当程序...
    99+
    2023-03-24
    go异常捕获panic recover go异常捕获 go panic函数使用
  • 一文带你掌握Golang中panic与recover的使用方法
    目录panicrecoverdefer/panic/recover示例panic panic作用是终止当前正在运行的程序(包括所有协程)并输出导致异常的堆栈信息。在遇到无法处理的异常...
    99+
    2023-05-18
    Golang panic recover使用方法 Golang panic recover Golang panic Golang recover
  • C++怎么实现Go的defer功能
    这篇文章主要介绍“C++怎么实现Go的defer功能”,在日常操作中,相信很多人在C++怎么实现Go的defer功能问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C++怎么实现Go的defer功能”的疑惑有所...
    99+
    2023-06-20
  • Go语言中defer语句怎么使用
    今天小编给大家分享一下Go语言中defer语句怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1.在一个函数内的def...
    99+
    2023-07-02
  • Go defer 原理和源码剖析是怎样的
    本篇文章为大家展示了Go defer 原理和源码剖析是怎样的,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。Go 语言中有一个非常有用的保留字 defer,它可以调用一个函数,该函数的执行被推迟到包裹...
    99+
    2023-06-25
  • GO语言延迟函数defer怎么使用
    今天小编给大家分享一下GO语言延迟函数defer怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。go语言中defer最...
    99+
    2023-07-05
  • 使用Go defer时要注意什么
    本篇内容介绍了“使用Go defer时要注意什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!使用 Go defer 要小心这 2 个折腾人...
    99+
    2023-06-20
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作