序
本文次要钻研一下 golang 的 panic 与 recover
panic 与 recover
recover 在如下三种状况下返回 nil
- panic 参数为 nil
- goroutine 没有产生 panic
- recover 不是在 defer func 中调用
实例
实例 1
var fc func() string
func protect(g func() string) {defer func() {fmt.Println("done") // Println executes normally even if there is a panic
if x := recover(); x != nil {fmt.Printf("run time panic: %v", x)
}
}()
fmt.Println("start")
fmt.Println(g())
}
func main() {protect(fc)
}
这里 fc 为 nil,因此产生 runtime panic,而后被 defer 的 recover 捕捉
实例 2
func deferRecover() {defer func() {fmt.Println("defer1: recover")
if err := recover(); err != nil {fmt.Println(err)
} else {fmt.Println("defer1 recover nil")
}
}()
defer func() {fmt.Println("defer2: recover")
if err := recover(); err != nil {fmt.Println(err)
}
}()
panic("manual panic")
defer func() {fmt.Println("never be executed")
}()}
这里 defer2 捕捉了 panic,defer1 就捕捉不到 panic 了
实例 3
func paincInDefer() {defer func() {fmt.Println("defer1 recover")
if err := recover(); err != nil {
// main panic is override by defer2 panic
fmt.Println(err)
} else {fmt.Println("defer1 recover nil")
}
}()
defer func() {fmt.Println("defer2 begin to panic")
panic("defer2 panic")
}()
panic("main panic")
}
这里 defer2 产生了 panic,” 笼罩 ” 了原始的 panic,defer1 捕捉的是最初一个 panic 即 defer2 产生的 panic
实例 4
func paincInDeferNotRecover() {defer func() {fmt.Println("defer1")
}()
defer func() {fmt.Println("defer2 begin to panic")
panic("defer2 panic")
}()
panic("main panic")
}
这里 defer2 产生了 panic,defer1 没有捕捉,产生的 panic 顺次显示了 main panic 及 defer2 panic
输入
defer2 begin to panic
defer1
panic: main panic
panic: defer2 panic
goroutine 1 [running]:
main.paincInDeferNotRecover.func2()
实例 5
func multiPaincInDefer() {defer func() {fmt.Println("defer1")
panic("defer3 panic")
}()
defer func() {fmt.Println("defer2 begin to panic")
panic("defer2 panic")
}()
panic("main panic")
}
输入
defer2 begin to panic
defer1
panic: main panic
panic: defer2 panic
panic: defer3 panic
goroutine 1 [running]:
main.multiPaincInDefer.func1()
实例 6
func panicAfterRecover() {defer func() {fmt.Println("defer1")
if err := recover(); err != nil {panic("defer1 panic after recover")
} else {fmt.Println("defer1 recover nil")
}
}()
defer func() {fmt.Println("defer2 begin to panic")
panic("defer2 panic")
}()
panic("main panic")
}
输入
defer2 begin to panic
defer1
panic: main panic
panic: defer2 panic [recovered]
panic: defer1 panic after recover
goroutine 1 [running]:
main.panicAfterRecover.func1()
/defer.go:180 +0x10d
panic(0x10b2020, 0x10eaed0)
/usr/local/go/src/runtime/panic.go:969 +0x1b9
main.panicAfterRecover.func2()
/defer.go:188 +0x95
panic(0x10b2020, 0x10eaee0)
/usr/local/go/src/runtime/panic.go:969 +0x1b9
main.panicAfterRecover()
/defer.go:191 +0x68
main.main()
/defer.go:215 +0x25
exit status 2
小结
- recover 必须在 defer 中调用才能够,多个 defer 呈现了 panic 没有 recover,则 panic 信息会列出所有的 painc 信息
- 如果 defer 也呈现了 panic 则若有被 recover 则 recover 的是最初一个 panic,非 defer 的 panic 被 ” 笼罩 ”
- 如果 defer 的 recover 之后呈现了 panic,则最初 panic 会呈现被 recover 的那个 panic 信息
doc
- Handling_panics