Golang中defer與recover的組合使用示例代碼
recover運(yùn)行的條件:
- 該協(xié)程必須出現(xiàn)了panic
- recover函數(shù)必須在和panic同級(jí)的defer中被調(diào)用
在Go語(yǔ)言中,defer
和recover
是兩個(gè)關(guān)鍵特性,通常結(jié)合使用以處理資源管理和異?;謴?fù)。以下是它們的核心應(yīng)用場(chǎng)景及使用示例:
1. defer 的應(yīng)用場(chǎng)景
defer
用于延遲執(zhí)行函數(shù)調(diào)用,確保在函數(shù)退出前執(zhí)行特定操作。主要用途包括:
資源釋放
文件操作:確保文件句柄關(guān)閉。
func readFile(filename string) error { file, err := os.Open(filename) if err != nil { return err } defer file.Close() // 確保函數(shù)返回前關(guān)閉文件 // 處理文件內(nèi)容... return nil }
鎖釋放:防止死鎖。
var mu sync.Mutex func updateData() { mu.Lock() defer mu.Unlock() // 函數(shù)退出時(shí)自動(dòng)釋放鎖 // 修改共享數(shù)據(jù)... }
事務(wù)回滾
- 數(shù)據(jù)庫(kù)或業(yè)務(wù)邏輯中,確保操作失敗時(shí)回滾。
func transferMoney() { tx := db.Begin() defer func() { if r := recover(); r != nil { // 結(jié)合recover處理panic tx.Rollback() } }() // 執(zhí)行轉(zhuǎn)賬操作,可能觸發(fā)panic tx.Commit() }
2. recover 的應(yīng)用場(chǎng)景
recover
用于捕獲panic
,防止程序非正常終止。必須在defer函數(shù)中調(diào)用。
全局異常恢復(fù)
- 防止因未處理的
panic
導(dǎo)致程序崩潰。func safeCall() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered from panic:", r) } }() // 可能觸發(fā)panic的代碼 panic("unexpected error") }
保護(hù)Goroutine
- 避免某個(gè)Goroutine的
panic
影響整個(gè)程序。func startWorker() { go func() { defer func() { if r := recover(); r != nil { log.Println("Worker panic:", r) } }() // Goroutine的業(yè)務(wù)邏輯... }() }
優(yōu)雅降級(jí)
- 將
panic
轉(zhuǎn)換為錯(cuò)誤,保持服務(wù)可用性。func safeHandler() (err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("internal error: %v", r) } }() // 可能panic的代碼 return nil }
3. 結(jié)合使用示例
func processRequest() (err error) { // 恢復(fù)panic并轉(zhuǎn)為錯(cuò)誤 defer func() { if r := recover(); r != nil { err = fmt.Errorf("panic occurred: %v", r) } }() // 資源管理示例 file, err := os.Open("data.txt") if err != nil { return err } defer file.Close() // 確保文件關(guān)閉 // 業(yè)務(wù)邏輯(可能觸發(fā)panic) if someCondition { panic("data corruption") } return nil }
4. 注意事項(xiàng)
recover
僅在defer
中有效:非defer
上下文中調(diào)用會(huì)返回nil
。- 避免濫用
recover
:隱藏panic
可能導(dǎo)致未知狀態(tài),應(yīng)僅在必要時(shí)使用。 - 明確錯(cuò)誤處理:優(yōu)先返回錯(cuò)誤而非依賴
panic
/recover
,后者適用于不可恢復(fù)的異常(如程序邏輯錯(cuò)誤)。
通過(guò)合理使用defer
和recover
,可以顯著提升Go程序的健壯性和可維護(hù)性,尤其在資源管理和異?;謴?fù)場(chǎng)景中。
在Go語(yǔ)言中,上述代碼無(wú)法正確捕獲panic,原因如下:
5. 問題分析
代碼示例
func safeCall() { // 直接調(diào)用recover(不在defer中) if r := recover(); r != nil { fmt.Println("Recovered from panic:", r) } // 觸發(fā)新的panic panic("unexpected error") } // 將safeCall包裹在defer中 defer func() { safeCall() }()
關(guān)鍵問題
recover未在defer中調(diào)用:
safeCall
中的recover
直接調(diào)用,而非通過(guò)defer
注冊(cè)的函數(shù)。此時(shí)recover
會(huì)在safeCall
正常執(zhí)行時(shí)立即觸發(fā),而非在panic
發(fā)生后被動(dòng)調(diào)用。- 若此時(shí)未發(fā)生
panic
,recover
返回nil
,無(wú)法捕獲后續(xù)觸發(fā)的panic
。
panic與defer執(zhí)行順序:
- 當(dāng)外層函數(shù)觸發(fā)
panic
時(shí),會(huì)先執(zhí)行已注冊(cè)的defer
函數(shù)。 - 在
defer
中調(diào)用safeCall
,此時(shí)safeCall
內(nèi)的recover
嘗試捕獲當(dāng)前panic
,但隨后safeCall
自身又觸發(fā)了一個(gè)新的panic("unexpected error")
,而新的panic未被任何recover處理,導(dǎo)致程序崩潰。
- 當(dāng)外層函數(shù)觸發(fā)
6. 正確寫法
修復(fù)方案
將recover
放在defer
函數(shù)中,并直接與可能觸發(fā)panic
的代碼關(guān)聯(lián):
func safeCall() { // 可能觸發(fā)panic的代碼 defer func() { // 在defer中調(diào)用recover if r := recover(); r != nil { fmt.Println("Recovered from panic:", r) } }() panic("unexpected error") } // 注冊(cè)defer defer safeCall()
執(zhí)行邏輯
- 調(diào)用
defer safeCall()
,注冊(cè)safeCall
到外層函數(shù)的defer
棧。 - 當(dāng)外層函數(shù)觸發(fā)
panic
時(shí),執(zhí)行safeCall
。 safeCall
內(nèi)部的defer
函數(shù)中的recover
會(huì)捕獲當(dāng)前panic
,阻止其繼續(xù)傳播。- 若
safeCall
自身觸發(fā)panic
,該panic
會(huì)被其自身的defer recover
捕獲。
7. 錯(cuò)誤示例的詳細(xì)解釋
原代碼執(zhí)行流程
假設(shè)外層函數(shù)觸發(fā)panic
:
- 外層函數(shù)執(zhí)行
panic("outer panic")
。 - 程序開始處理
defer
,調(diào)用defer func() { safeCall() }()
。 safeCall
執(zhí)行:recover()
嘗試捕獲外層panic("outer panic")
,打印恢復(fù)信息。- 隨后觸發(fā)新的
panic("unexpected error")
。
- 新的panic未被任何recover處理,導(dǎo)致程序崩潰。
關(guān)鍵結(jié)論
recover
必須通過(guò)defer
注冊(cè)的函數(shù)被動(dòng)調(diào)用,才能捕獲到panic
。- 若在普通代碼中直接調(diào)用
recover
,只有在已發(fā)生panic
且未被處理時(shí)才會(huì)生效。
總結(jié)
- 必須將recover放在defer函數(shù)中,才能確保在
panic
發(fā)生后被動(dòng)調(diào)用。 - 避免在恢復(fù)邏輯中觸發(fā)新的panic,否則需要額外的
recover
處理。 - 正確的
defer
和recover
組合是資源管理和異?;謴?fù)的核心模式。
通過(guò)調(diào)整代碼結(jié)構(gòu),確保recover
在defer
中調(diào)用,即可正確捕獲并處理panic
。
到此這篇關(guān)于Golang中defer與recover組合使用的文章就介紹到這了,更多相關(guān)Go defer與recover組合使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語(yǔ)音開發(fā)中常見Error類型處理示例詳解
這篇文章主要為大家介紹了Go語(yǔ)音開發(fā)中常見Error類型處理示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12goland 實(shí)現(xiàn)websocket server的示例代碼
本文主要介紹了goland 實(shí)現(xiàn)websocket server的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06使用go語(yǔ)言解析xml的實(shí)現(xiàn)方法(必看篇)
下面小編就為大家?guī)?lái)一篇使用go語(yǔ)言解析xml的實(shí)現(xiàn)方法(必看篇)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-06-06GO 使用Webhook 實(shí)現(xiàn)github 自動(dòng)化部署的方法
這篇文章主要介紹了GO 使用Webhook 實(shí)現(xiàn)github 自動(dòng)化部署的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05