golang中defer,panic,recover是很常用的三個(gè)特性莹妒,三者一起使用可以充當(dāng)其他語言中try…catch…的角色旨怠,而defer本身又像其他語言的析構(gòu)函數(shù)
defer延遲執(zhí)行:
defer 是return 后才調(diào)用
4. 為什么要有 defer鉴腻?
看完上面的例子后爽哎,不知道你是否和我一樣倦青,對這個(gè)defer的使用效果感到熟悉盹舞?貌似在 Python 也見過類似的用法。
雖然 Python 中沒有 defer 癣亚,但是它有 with 上下文管理器获印。我們知道在 Python 中可以使用 defer 實(shí)現(xiàn)對資源的管理。最常用的例子就是文件的打開關(guān)閉玻孟。
你可能會(huì)有疑問鳍征,這也沒什么意義呀,我把這個(gè)放在 defer 執(zhí)行的函數(shù)放在 return 那里執(zhí)行不就好了匣掸。
固然可以氮双,但是當(dāng)一個(gè)函數(shù)里有多個(gè) return 時(shí),你得多調(diào)用好多次這個(gè)函數(shù)送爸,代碼就臃腫起來了碱璃。
若是沒有 defer,你可以寫出這樣的代碼
func f() { r := getResource() //0肛真,獲取資源 ...... if ... { r.release() //1爽航,釋放資源 return } ...... if ... { r.release() //2,釋放資源 return } ...... if ... { r.release() //3历极,釋放資源 return } ...... r.release() //4衷佃,釋放資源 return }
使用了 defer 后,代碼就顯得簡單直接锄列,不管你在何處 return邻邮,都會(huì)執(zhí)行 defer 后的函數(shù)克婶。
func f() { r := getResource() //0,獲取資源 defer r.release() //1鸭蛙,釋放資源 ...... if ... { ... return } ...... if ... { ... return } ...... if ... { ... return } ...... return }
還是類似于析構(gòu)函數(shù)筋岛,多個(gè)if分支和return的情況下,延遲最后執(zhí)行
panic, 手動(dòng)觸發(fā)宕機(jī)
1. 觸發(fā)panic
手動(dòng)觸發(fā)宕機(jī),是非常簡單的一件事揩晴,只需要調(diào)用 panic 這個(gè)內(nèi)置函數(shù)即可,就像這樣子
package main func main() { panic("crash") }
運(yùn)行后诅愚,直接報(bào)錯(cuò)宕機(jī)
$ go run main.go go run main.go panic: crash goroutine 1 [running]: main.main() E:/Go-Code/main.go:4 +0x40 exit status 2
2. 捕獲 panic
發(fā)生了異常违孝,有時(shí)候就得捕獲,就像 Python 中的except 一樣喇喉,那 Golang 中是如何做到的呢校坑?
這就不得不引出另外一個(gè)內(nèi)建函數(shù) -- recover,它可以讓程序在發(fā)生宕機(jī)后起生回生膏斤。
但是 recover 的使用邪驮,有一個(gè)條件,就是它必須在 defer 函數(shù)中才能生效沮榜,其他作用域下敞映,它是不工作的。
這是一個(gè)簡單的例子
package main
import "fmt"
func set_data(x int){
defer func(){
if err := recover(); err != nil {
fmt.Println(err)
}
}()
var arr [10]int
arr[x] = 88
}
func main(){
set_data(20)
fmt.Println("is ok")
}
如果不使用defer 匿名函數(shù) + recover()振愿,那么程序會(huì)直接中斷冕末,提示
panic: runtime error: index out of range [20] with length 10
使用了recover()的執(zhí)行結(jié)果為:
3. 無法跨協(xié)程
從上面的例子档桃,可以看到憔晒,即使 panic 會(huì)導(dǎo)致整個(gè)程序退出,但在退出前嘹屯,若有 defer 延遲函數(shù)从撼,還是得執(zhí)行完 defer 。
但是這個(gè) defer 在多個(gè)協(xié)程之間是沒有效果婆翔,在子協(xié)程里觸發(fā) panic,只能觸發(fā)自己協(xié)程內(nèi)的 defer啃奴,而不能調(diào)用 main 協(xié)程里的 defer 函數(shù)的。
來做個(gè)實(shí)驗(yàn)就知道了
import ( "fmt" "time" ) func main() { // 這個(gè) defer 并不會(huì)執(zhí)行 defer fmt.Println("in main") go func() { defer println("in goroutine") panic("") }() time.Sleep(2 * time.Second) }
輸出如下
in goroutine panic:
Golang 異常的拋出與捕獲畔咧,依賴兩個(gè)內(nèi)置函數(shù):
panic:拋出異常揖膜,使程序崩潰
recover:捕獲異常,恢復(fù)程序或做收尾工作
revocer 調(diào)用后拜隧,拋出的 panic 將會(huì)在此處終結(jié),不會(huì)再外拋洪添,但是 recover干奢,并不能任意使用盏袄,它有強(qiáng)制要求,必須得在 defer 下才能發(fā)揮用途辕羽。