panic 函數(shù) 和 recover 函數(shù)
panic
和 recover
在使用方法上更接近于 try/catch 結構化異常:
func panic(v interface{})
func recover() interface{}
panic
-
go 中協(xié)程是平等的腔寡,如果一個協(xié)程中產生的 panic 沒有處理掉贸呢,會導致整個程序奔潰
package main func main() { go func() { panic("hello world") }() select {} }
image.png -
panic 會遞歸的執(zhí)行本協(xié)程中所有的defer领猾,與函數(shù)正常退出時執(zhí)行的順序一致
func main() { defer fmt.Print("A") defer fmt.Print("B") fmt.Print("C") panic("demo") defer fmt.Print("D") }
image.png -
panic 不會調用其他協(xié)程中的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協(xié)程提前退出 }
image.png -
如果
panic
在執(zhí)行過程再次發(fā)生panic
谈宛,程序立即終止當前的defer
函數(shù)的執(zhí)行掐场,然后繼續(xù)接下來的panic
流程溅话,只是當前的defer
函數(shù)中的panic
后面的語句就沒有機會執(zhí)行了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
-
當函數(shù)中發(fā)生panic 并用 recover 恢復后,無法回到本函數(shù)發(fā)生panic的位置繼續(xù)執(zhí)行奈偏, 同時如果本函數(shù)有匿名返回值坞嘀,則直接返回相應類型的零值,對于具名返回值惊来,函數(shù)將返回當前已經存在的值丽涩。
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 函數(shù)必須且直接位于defer函數(shù)中才有效
func main() { defer func() { func() { if err := recover(); err != nil { fmt.Println("A") } }() }() panic("demo") fmt.Println("B") }
上面這種寫法就不會生效裁蚁。
image.png