从零开始学Go之函数(三):宕机与恢复
延迟执行defer:
当某些语句需要函数结束时才使用时,可以使用defer语句
最常用的就是在打开os.Open后加入Close()时用defer避免程序结束未关闭文件
defer 语句
例子:
func main() { fmt.Println("test") for i:=0;i<3 ;i++ { fmt.Println(i) defer fmt.Println(i) } }
运行结果:
test
0
1
2
2
1
0
文件操作例子:
file, err := os.Open(filename) if err != nil { return err } /* 这里defer要写在err判断的后边而不是os.Open后边 如果资源没有获取成功,就没有必要对资源执行释放操作 如果err不为nil而执行资源执行释放操作,有可能导致panic */ defer file.Close()
defer 语句会将函数推迟到外层函数返回之后执行。
推迟调用的函数其参数会立即求值,但直到外层函数返回前该函数都不会被调用。推迟的函数调用会被压入一个栈中(后进先出)。
当外层函数返回时,被推迟的函数会按照后进先出的顺序调用,可能会修改返回值从而得出不正确的结果。
宕机panic:
当需要在程序中手动触发宕机时,可以用内置的panic语句
func panic(v interface{})
在panic后剩下的代码都会不会执行,同时会将堆栈和 goroutine 信息输出到控制台
func main() { fmt.Println("位置1") defer fmt.Println("defer位置1") fmt.Println("位置2") panic("手动崩溃") defer fmt.Println("defer位置2") fmt.Println("位置3") }
运行结果
panic: 手动崩溃
位置1
位置2
defer位置1
goroutine 1 [running]:
main.main()
D:/SoloWork/Learn/main.go:13 +0x13a
恢复recover:
recover是用来panic时使程序继续运行的函数
func recover() interface{}
使用方法是在panic前先defer声明一个闭包函数,让里面的recover捕捉panic异常,这样当后面遇到不重要的panic时能抛出错误又能让程序继续运行来处理这个错误。
recover之后,逻辑并不会恢复到panic那个点去,函数还是会在defer之后返回,也就是说当前函数会中止,但上层函数会继续运行。
例子:
func main() { fmt.Println("位置1") defer fmt.Println("defer位置1") fmt.Println("位置2") defer func() { // 必须要先声明defer,否则不能捕获到panic异常 fmt.Println("defer进入函数") if err := recover(); err != nil { fmt.Println(err) // 这里的err其实就是panic传入的内容 } fmt.Println("defer错误处理") }() panic("崩溃") //这里开始向下不会执行 defer fmt.Println("defer位置2") fmt.Println("位置3") }
运行结果:
位置1
位置2
defer进入函数
崩溃
defer错误处理
defer位置1
func ContinueRun(){ defer func() { // 必须要先声明defer,否则不能捕获到panic异常 fmt.Println("defer进入函数") if err := recover(); err != nil { fmt.Println(err) // 这里的err其实就是panic传入的内容 } fmt.Println("defer错误处理") }() panic("崩溃") fmt.Println("位置4") } func main() { fmt.Println("位置1") defer fmt.Println("defer位置1") fmt.Println("位置2") ContinueRun() defer fmt.Println("defer位置2") fmt.Println("位置3") }
运行结果:
位置1
位置2
defer进入函数
崩溃
defer错误处理
位置3
defer位置2
defer位置1