panic 函数 和 recover 函数
panic
和 recover
在使用方法上更接近于 try/catch 结构化异常:
func panic(v interface{})
func recover() interface{}
panic
-
go 中协程是平等的,如果一个协程中产生的 panic 没有处理掉,会导致整个程序奔溃
package main func main() { go func() { panic("hello world") }() select {} }
image.png -
panic 会递归的执行本协程中所有的defer,与函数正常退出时执行的顺序一致
func main() { defer fmt.Print("A") defer fmt.Print("B") fmt.Print("C") panic("demo") defer fmt.Print("D") }
image.png -
panic 不会调用其他协程中的defer
func foo() { defer fmt.Print("A") defer fmt.Print("B") fmt.Print("C") panic("foo demo") defer fmt.Print("D") } func main() { defer func () { fmt.Print("main demo") }() go foo() select {} // 防止main协程提前退出 }
image.png -
如果
panic
在执行过程再次发生panic
,程序立即终止当前的defer
函数的执行,然后继续接下来的panic
流程,只是当前的defer
函数中的panic
后面的语句就没有机会执行了func main() { defer func() { recover() }() defer fmt.Println("A") defer func() { fmt.Print("B") panic("panic in defer") fmt.Print("C") }() panic("panic") fmt.Print("D") }
image.png
注意 C 没有输出。
recover
-
当函数中发生panic 并用 recover 恢复后,无法回到本函数发生panic的位置继续执行, 同时如果本函数有匿名返回值,则直接返回相应类型的零值,对于具名返回值,函数将返回当前已经存在的值。
func foo() { defer fmt.Println("C") defer func() { if err := recover(); err != nil { fmt.Print("A") } }() panic("demo") fmt.Println("B") } func main() { foo() }
image.png注意没有输出B
-
panic 被 recover 之后,无法再次被 recover 捕获
func foo() { defer func() { if err := recover(); err != nil { fmt.Print("A") } fmt.Print("a") }() defer func() { if err := recover(); err != nil { fmt.Print("B") } }() panic("demo") fmt.Print("B") } func main() { foo() }
image.png
- recover 函数必须且直接位于defer函数中才有效
func main() { defer func() { func() { if err := recover(); err != nil { fmt.Println("A") } }() }() panic("demo") fmt.Println("B") }
上面这种写法就不会生效。
image.png