golang的defer,panic,recover简单举例
golang中defer,panic,recover是很常用的三个特性,三者一起使用可以充当其他语言中try…catch…的角色,而defer本身又像其他语言的析构函数
defer延迟执行:
defer 是return 后才调用
4. 为什么要有 defer?
看完上面的例子后,不知道你是否和我一样,对这个defer的使用效果感到熟悉?貌似在 Python 也见过类似的用法。
虽然 Python 中没有 defer ,但是它有 with 上下文管理器。我们知道在 Python 中可以使用 defer 实现对资源的管理。最常用的例子就是文件的打开关闭。
你可能会有疑问,这也没什么意义呀,我把这个放在 defer 执行的函数放在 return 那里执行不就好了。
固然可以,但是当一个函数里有多个 return 时,你得多调用好多次这个函数,代码就臃肿起来了。
若是没有 defer,你可以写出这样的代码
func f() { r := getResource() //0,获取资源 ...... if ... { r.release() //1,释放资源 return } ...... if ... { r.release() //2,释放资源 return } ...... if ... { r.release() //3,释放资源 return } ...... r.release() //4,释放资源 return }
使用了 defer 后,代码就显得简单直接,不管你在何处 return,都会执行 defer 后的函数。
func f() { r := getResource() //0,获取资源 defer r.release() //1,释放资源 ...... if ... { ... return } ...... if ... { ... return } ...... if ... { ... return } ...... return }
还是类似于析构函数,多个if分支和return的情况下,延迟最后执行
panic, 手动触发宕机
1. 触发panic
手动触发宕机,是非常简单的一件事,只需要调用 panic 这个内置函数即可,就像这样子
package main func main() { panic("crash") }
运行后,直接报错宕机
$ go run main.go go run main.go panic: crash goroutine 1 [running]: main.main() E:/Go-Code/main.go:4 +0x40 exit status 2
2. 捕获 panic
发生了异常,有时候就得捕获,就像 Python 中的except 一样,那 Golang 中是如何做到的呢?
这就不得不引出另外一个内建函数 -- recover,它可以让程序在发生宕机后起生回生。
但是 recover 的使用,有一个条件,就是它必须在 defer 函数中才能生效,其他作用域下,它是不工作的。
这是一个简单的例子
package main
import "fmt"
func set_data(x int){
defer func(){
if err := recover(); err != nil {
fmt.Println(err)
}
}()
var arr [10]int
arr[x] = 88
}
func main(){
set_data(20)
fmt.Println("is ok")
}
如果不使用defer 匿名函数 + recover(),那么程序会直接中断,提示
panic: runtime error: index out of range [20] with length 10
使用了recover()的执行结果为:
3. 无法跨协程
从上面的例子,可以看到,即使 panic 会导致整个程序退出,但在退出前,若有 defer 延迟函数,还是得执行完 defer 。
但是这个 defer 在多个协程之间是没有效果,在子协程里触发 panic,只能触发自己协程内的 defer,而不能调用 main 协程里的 defer 函数的。
来做个实验就知道了
import ( "fmt" "time" ) func main() { // 这个 defer 并不会执行 defer fmt.Println("in main") go func() { defer println("in goroutine") panic("") }() time.Sleep(2 * time.Second) }
输出如下
in goroutine panic:
Golang 异常的抛出与捕获,依赖两个内置函数:
panic:抛出异常,使程序崩溃
recover:捕获异常,恢复程序或做收尾工作
revocer 调用后,抛出的 panic 将会在此处终结,不会再外抛,但是 recover,并不能任意使用,它有强制要求,必须得在 defer 下才能发挥用途。