Golang---Defer, Panic, and Recover
来源:互联网 发布:超级肉食男孩 mac 编辑:程序博客网 时间:2024/06/05 10:45
Go
语言中保留有传统的控制流程,比如:if
, for
, switch
,甚至连 goto
都有。另外 goroutine
语法也可以实现面向过程的多线程开发。在这里我们将要讨论一些同其它语言不相同的语句:defer
, panic
, 和 recover
defer
语句可以将一个函数放入一个链表。当调用这个 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
语句可以保证打开打开文档之后,能够正确的关闭文件,而不去考虑在之后的的逻辑中有多少个 return
语句。
defer
语句的行为直观明了,并且有三个简单的特性:
deferred
函数参数初始化的时机为defer
语句声明时在下面的例子中,当
Println
被调用时,i
当前的值已经传递到函数中,所以值为0
func a() { i := 0 defer fmt.Println(i) i++ return }
- 当外围函数返回时,
deferred
函数按照 后进先出[LIFO] 的顺序执行
下面的例子将会打印3210
func b() { for i := 0; i < 4; i++ { defer fmt.Print(i) } }
defered
函数可以读取外围函数的返回值,并对其进行赋值操作
下面的例子中,defered
函数增加了外围函数的返回值,所以,最后的返回结果为2
func c() (i int) { defer func() { i++ }() return 1 }
这对于改变程序返回错误是非常方便的。在下面会举一个简单的例子。
Panic 是内置函数,终止当前的流程,使之开始变得 panicking
。例如,当函数 F
调用 panic
, F
将会停止执行,任何 deferred
函数将会正常执行,之后 F
返回到调用它的函数。对于调用者而言,F
就相当于一个 panic
。这一过程持续向上直到程序崩溃。 Panics
可以通过直接调用 panic
关键字实现,也可以通过 runtime errors
发生,例如数组越界。
Recover 也是内置函数,用来恢复遇到 panic
的 goroutine
。Recover
仅仅在 deferred
函数中才有意义。如果,在正常的函数中调用,recover
将会返回 nil
并且没有其它任何效果。如果当前的 goroutine
产生了 panic
, recover
将会捕获传递给 panic
的数据,并且恢复到正常的执行状态。
下面的程序将会说明 panic
defer
recover
的机制。
package mainimport "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
根据 i
的值执行不同的动作,当 i
大于 3
时会主动产生 panic
。 函数 f
通过 deferred
函数,使用 recover
捕获异常,如果不为空,则打印恢复的数据。在继续读以下内容之前,试着想想输出的内容。
程序的输出结果为:
Calling g.Printing in g 0Printing in g 1Printing in g 2Printing in g 3Panicking!Defer in g 3Defer in g 2Defer in g 1Defer in g 0Recovered in f 4Returned normally from f.
如果我们将函数 f
中的 deferred
函数去掉,panic
将不会被捕获,整个程序将会崩溃。修改后的程序如下:
Calling g.Printing in g 0Printing in g 1Printing in g 2Printing in g 3Panicking!Defer in g 3Defer in g 2Defer in g 1Defer in g 0panic: 4panic PC=0x2a9cd8[stack trace omitted]
可以从 Go
的标准库 json package 中获取 panic
和 recover
的真实例子。
Go
语言库的方便之处就在于,即使库种使用了 panic
,外部 API
仍然会返回明确的出错信息,并不会引起 panic
defer
的其它应用(不是之前提到的 file.Close
)包括释放锁:
mu.Lock()defer mu.Unlock()
打印页脚
printHeader()defer printFooter()
等等…
总而言之,defer
语句(结合 panic
和 recover
与否)提供一种不同寻常强大的控制机制。它可以通过不同多样的建模实现其它语言的特殊逻辑控制。不妨试一试哦。
By Andrew Gerrand
原文网址
译者注:defer
在某种程序上更像finally
, panic
在某种程度上更像 throw exception
, recover
在某种程度上更像 catch
- Golang---Defer, Panic, and Recover
- Defer, Panic, and Recover
- Defer, Panic, and Recover
- golang中的defer panic recover
- Golang中的defer, panic, recover
- [GoBlog] Defer, Panic, and Recover
- Understanding Defer, Panic and Recover
- 【GoLang】panic defer recover 深入理解
- golang之defer,panic,recover用法
- golang 使用defer、panic、recover的问题
- Go-defer,panic,recover
- GO defer panic recover
- 理解Defer、Panic和Recover
- golang中的defer panic使用方法
- Go的异常处理 defer, panic, recover
- Go的异常处理 defer, panic, recover
- Go的异常处理 defer, panic, recover
- Go的异常处理 defer, panic, recover
- 瀑布流、鼠标滑动加载
- 2015年大二上-数据结构-数组与广义表(2)-3.上三角矩阵的压缩存储及基本运算
- 望大家互相指教,每个html语言必须的基本结构
- JD项目的借鉴(1)
- 表格奇偶行变色
- Golang---Defer, Panic, and Recover
- C插入排序法实现
- BZOJ-1008 越狱 数论快速幂
- 设置mysql允许外部IP连接的解决方法
- 对拍怎么写
- semiJoin mapreduce
- 初探nginx反向代理
- JAVA开发环境变量配置
- 环境变量 $PATH