panic和recover
有些错误编译期就能发现,编译的时候编译器就会报错,而有些运行时的错误编译器是没办法发现的。golang的运行时异常叫做panic
。
在golang当切片访问越界,空指针引用等会引起panic
,panic
如果没有自己手动捕获的话,程序会中断运行并打印panic
信息。
var a []int = []int{0, 1, 2}
fmt.Println(a[3]) //panic: runtime error: index out of range [3] with length 3
type A struct {
Name string
}
var a *A
fmt.Println(a.Name) //panic: runtime error: invalid memory address or nil pointer dereference
手动panic
当程序运行中出现一些异常我们需要程序中断的时候我们可以手动panic
var model = os.Getenv("MODEL") //获取环境变量 MODEL
if model == "" {
panic("no value for $MODEL") //MODEL 环境变量未设置程序中断
}
使用recover捕获panic
我们在defer
修饰的函数里面使用recover
可以捕获的panic
。
func main() {
func() {
defer func() {
if err := recover(); err != nil { //recover 函数没有异常的是返回 nil
fmt.Printf("panic: %v ", err)
}
}()
func() {
panic("panic") //函数执行出现异常
}()
}()
fmt.Println("here")
}
执行结果是
anic: panic
here
panic
的产生后会终止当前函数运行,然后去检测当前函数的defer
是否有recover
,没有的话会一直往上层冒泡直至最顶层;如果中间某个函数的defer有recover
则这个向上冒泡过程到这个函数就会终止。
golang中goroutine
没有父子关系,不能在一个goroutine
中 recover另一个goroutine
的 panic
。
func main() {
func() {
defer func() {
if err := recover(); err != nil {
fmt.Printf("panic: %v ", err)
}
}()
go func() {
panic("panic")
}()
}()
time.Sleep(1e9)
fmt.Println("here")
}
执行结果是
panic: panic
goroutine 6 [running]:
main.main.func1.2()
/workspace/gocode/test/cmd/test.go:16 +0x39
created by main.main.func1
/workspace/gocode/test/cmd/test.go:15 +0x57
exit status 2
我们看到虽然recover 捕获了错误信息,但是程序还是退出了。