文档章节

go语言中的defer、panic、recover处理异常

DEC_LIU
 DEC_LIU
发布于 2013/06/14 10:58
字数 1074
阅读 320
收藏 1

go语言中的defer就像c++中的析构函数,但是go语言中defer的对象是函数(或者对象的方法),defer能保证在函数结束最后执行该方法(函数),但是有例外:如果在定义的方法中defer定义的方法如果在panic后面,defer定义的方法就无法执行到。

panic 是用来表示非常严重的不可恢复的错误的。在Go语言中这是一个内置函数,接收一个interface{}类型的值作为参数。panic 的作用就像我们平常接触的异常。不过Go可没有try…catch,所以,panic一般会导致程序挂掉(除非recover)。所以,Go语言中的异 常,那真的是异常了。你可以试试,调用panic看看,程序立马挂掉,然后Go运行时会打印出调用栈。
但是,关键的一点是,即使函数执行的时候 panic了,函数不往下走了,运行时并不是立刻向上传递panic,而是到defer那,等defer的东西都跑完了,panic再向上传递。所以这时 候 defer 有点类似 try-catch-finally 中的 finally。
panic就是这么简单。抛出个真正意义上的异常。

panic的函数并不会立刻返回,而是先defer,再返回,如果有办法将panic捕获到,并阻止panic传递,就正常处理,如果没有没有捕获,程序直接异常终止(可以注释掉下面程序中的recover试一试)。

Go语言提供了recover内置函数,前面提到,一旦panic,逻辑就会走到defer那,那我们就在defer那等着,调用recover函 数将会捕获到当前的panic(如果有的话),被捕获到的panic就不会向上传递了,于是,世界恢复了和平。你可以干你想干的事情了。

不过要注意的是,recover之后,逻辑并不会恢复到panic那个点去,函数还是会在defer之后返回。

请看下面的例子:

// go_exception project main.go

package main
import (
    "fmt"
)
func f() {
    defer func() {
        fmt.Println("inner func defer")
    }()
    fmt.Println("A")
    panic(3)
//panic方法后的方法、defer定义的方法都无法执行,也包括函数f()后面的任何方法以及defer定义的方法 
    defer func() {
        fmt.Println("inner func defer 1")
    }()
    //以下2个不能执行
    fmt.Println("b")
    fmt.Println("c")
}
func main() {
    fmt.Println("Hello World!")
    defer func() {
        fmt.Println("FUCK——1")
    }()
    defer func() {
        fmt.Println("d")
        if err := recover(); err != nil {
            fmt.Println(err)
        }
        fmt.Println("e")
    }()
    defer func() {
        fmt.Println("FUCK——2")
    }()
    f()
    //在f()函数以后的defer函数也不能执行
    defer func() {
        fmt.Println("FUCK——3")
    }()
}
运行输出结果:

E:/go_project/src/go_exception/go_exception.exe [E:/go_project/src/go_exception]

Hello World!

A

inner func defer

FUCK——2

d

3

e

FUCK——1

成功: 进程退出代码 0.

结果输出结果大家可以更好的理解defer、panic、revover处理机制了。

将上面的例子改成:

// go_exception project main.go

package main
import (
    "fmt"
)
func f() {
    defer func() {
        fmt.Println("inner func defer")
        if err := recover(); err != nil {
            fmt.Println(err, " fuck")
        }
    }()
    fmt.Println("A")
    panic(3)
    defer func() {
        fmt.Println("inner func defer 1")
    }()
    //以下2个不能执行
    fmt.Println("b")
    fmt.Println("c")
}
func main() {
    fmt.Println("Hello World!")
    defer func() {
        fmt.Println("FUCK——1")
    }()
    defer func() {
        fmt.Println("d")
        if err := recover(); err != nil {
            fmt.Println(err)
        }
        fmt.Println("e")
    }()
    defer func() {
        fmt.Println("FUCK——2")
    }()
    f()
    //在f()函数以后的defer函数也不能执行
    defer func() {
        fmt.Println("FUCK——3")
    }()
}
输出:

E:/go_project/src/go_exception/go_exception.exe [E:/go_project/src/go_exception]

Hello World!

A

inner func defer

3 fuck

FUCK——3

FUCK——2

d

e

FUCK——1

成功: 进程退出代码 0.

从以上结果可以看出,如果panic被recover捕获接收到,panic后的方法还是能继续执行的。

在看到许式伟的defer、panic、recover处理错误异常的时候,文章中有以下文字描述:

“当在一个函数执行过程中调用panic()函数时,正常的函数执行流程将立即终止,但函数中
之前使用defer 关键字延迟执行的语句将正常展开执行,之后该函数将返回到调用函数,并导致
逐层向上执行panic流程,直至所属的goroutine 中所有正在执行的函数被终止。错误信息将被报
告,包括在调用panic()函数时传入的参数,这个过程称为错误处理流程。”

执行完例子后对这句话理解的更深刻了。

总结:不建议使用panic、recover,除非必须要用。

© 著作权归作者所有

DEC_LIU
粉丝 27
博文 18
码字总数 9469
作品 0
浦东
架构师
私信 提问
golang捕获异常

Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,...

吴之恒心
2017/03/01
0
0
golang学习的点点滴滴:异常处理 defer, panic, recover

Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,...

亓斌哥哥
2014/09/29
90
0
Go的异常处理 defer, panic, recover

Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,...

blacklovebear
2014/02/09
82
0
Golang 异常处理机制——defer, error, panic, recover

一、前言 在实际的项目中,对于异常的最佳实践很多,在使用不同的语言开发不同类型的程序时,有不同的建议。Google C++ Style 中提到 Google 内部的 C++ 代码中不使用异常,社区也有很多关于...

吃一堑消化不良
2016/12/01
1K
0
Go基础编程:异常处理(error接口、panic、recover)

1 error接口 Go语言引入了一个关于错误处理的标准模式,即error接口,它是Go语言内建的接口类型,该接口的定义如下: Go语言的标准库代码包errors为用户提供如下方法: 另一个可以生成error...

tennysonsky
2018/01/01
0
0

没有更多内容

加载失败,请刷新页面

加载更多

CSS--列表

一、列表标识项 list-style-type none:去掉标识项 disc:默认实心圆 circle:空心圆 squire:矩形 二、列表项图片 list-style-img: 取值:url(路径) 三、列表项位置 list-style-position:...

wytao1995
今天
4
0
linux 命令-文本比较comm、diff、patch

本文原创首发于公众号:编程三分钟 今天学了三个文本比较的命令分享给大家。 comm comm 命令比较相同的文本 $ cat charabc$ cat chardiffadc 比如,我有两个文件char和chardiff如上,...

编程三分钟
今天
7
0
QML教程

https://blog.csdn.net/qq_40194498/article/category/7580030 https://blog.csdn.net/LaineGates/article/details/50887765...

shzwork
今天
5
0
HA Cluster之5

对于使用heartbeat v2版的CRM配置的集群信息都是保存在一个名为cib.xml的配置文件中,存放在/var/lib/heartbeat/crm/下。CIB:Cluster Information Base,由于xml文件配置不是那么方便,所以...

lhdzw
今天
6
0
玩转Redis-Redis基础数据结构及核心命令

  《玩转Redis》系列文章主要讲述Redis的基础及中高级应用,文章基于Redis5.0.4+。本文主要讲述Redis的数据结构String,《玩转Redis-Redis基础数据结构及核心命令》相关操作命令为方便对比...

zxiaofan666
今天
11
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部