一、panic()
通过panic()可以手动触发宕机,此时会让程序崩溃,并将堆栈和 goroutine 信息输出到控制台。
panic 特性:
panic()前如果有defer语句,则会在宕机退出前先把defer语句执行完;panic()后的语句不会再继续执行。
例1
package main
import "fmt"
func main() {
fmt.Println("before")
panic("panic")
fmt.Println("after")
}
控制台:
例2
func main() {
defer fmt.Println("宕机1")
defer fmt.Println("宕机2")
panic("panic")
defer fmt.Println("宕机3")
defer fmt.Println("宕机4")
}
二、recover()
panic() 抛出的错误可被 recover() 捕捉到。
package main
import (
"fmt"
"runtime"
)
type panicContext struct {
function string // 所在函数
}
func foo(bar func()) {
// 延迟处理的函数.注意整体的执行顺序为:bar()正常语句 -> panic() -> defer 函数内 recover() 等 -> 正式宕机
defer func() {
err := recover()
switch err.(type) {
case runtime.Error:
fmt.Println("runtime error:",err)
default:
fmt.Println("error:",err)
}
}()
// 用来触发 panic
bar()
}
func main() {
fmt.Println("运行前")
// 调用两次
// 第一次,手动触发 panic
foo(func() {
fmt.Println("手动宕机前")
// 使用 panic() 传递给 recover() 上下文
panic(&panicContext{
"手动触发 panic",
})
fmt.Println("手动宕机后")
})
// 第二次,故意空指针访问造成运行时错误
foo(func() {
fmt.Println("空指针运行时宕机前")
var a *int
*a =1
fmt.Println("空指针运行时宕机后")
})
fmt.Println("运行后")
}
总结:
- 从未执行
fmt.Println("手动宕机后") 和
fmt.Println("空指针运行时宕机后")
可知一个函数内panic后的语句不再执行。
2. 执行了
fmt.Println("运行后")
可知,panic 配合 recover 不会让程序宕机,而是继续执行。