golang之資源釋放/異常錯誤處理解析
資源釋放-defer
類似于其他語言的 析構(gòu)函數(shù) 比如PHP的 __destruct
func b() error {
resp, err := http.Get("xxx.com")
// 先判斷操作是否成功
if err != nil {
return err
}
// 如果操作成功,再進行Close操作
defer resp.Body.Close()
return nil
}從上面的示例可看到,defer 是個延遲函數(shù)(延遲執(zhí)行)
關(guān)于 defer 的一些常見誤區(qū)和陷阱
defer 語句是用來延遲執(zhí)行函數(shù)的,且延遲發(fā)生在函數(shù) return 之后具體指: Go return 在底層等于: 返回值入棧, defer函數(shù)調(diào)用, return
func returnValues() int {
var result int
defer func() {
// defer 匿名函數(shù)對變量的修改發(fā)生在 return 之后
result++
fmt.Println("defer")
}()
return result
}
// 如下輸出 0
fmt.Println(returnValues())go允許多個defer同時存在
defer 執(zhí)行順序遵循先進后出 first-in-last-out (FILO)屬于棧操作,如下輸出:3、2、1
func stackingDefers() {
defer func() {
fmt.Println("1")
}()
defer func() {
fmt.Println("2")
}()
defer func() {
fmt.Println("3")
}()
}函數(shù)被 deferred 時涉及的 參數(shù)變量值 在 defer 函數(shù)編寫時確定,而非實際調(diào)用時
func c() {
i := 0
defer fmt.Println(i) // 此時i變量值已被確定,即0
i++
defer fmt.Println(i) // 此時i++已執(zhí)行,變量值已被確定為 1
}
// 輸出 1 0;因為defer的執(zhí)行順序為 后進先出,所以先輸出 i++ 之后的 1;再輸出之前的 0defer與閉包陷阱
先來看一個示例:
func main() {
i := 1
defer func() {
fmt.Println("defer-閉包i值:", i)
}()
defer fmt.Println("defer-i值:", i)
i += 100
}結(jié)果輸出為:
defer-i值: 1
defer-閉包i值: 101
為啥捏?
- 第二個defer,因為函數(shù)被 deferred 時涉及的 參數(shù)變量值 在 defer 函數(shù)編寫時已確定,所以輸出
defer-i值: 1 - 第一個defer,閉包中的參數(shù)傳遞是引用類型傳遞,因此匿名函數(shù)中的 i 可以輸出 101(匿名函數(shù)都是閉包,按引用類型傳遞)
try-catch-finally
Go追求簡潔優(yōu)雅,所以不支持傳統(tǒng)的 try-catch-finally 結(jié)構(gòu),因為Go語言的設(shè)計者們認為,將異常與控制結(jié)構(gòu)混在一起很容易使得代碼變混亂(咱不懂,但內(nèi)心大受震撼)。
如果要在go實現(xiàn)這么個邏輯,需要用到這么幾個關(guān)鍵詞:defer, panic, recover如何使用 defer 執(zhí)行 try-catch-finally 調(diào)試:
func main() {
// 類似其他語言的 try (隱式包含)
defer func() {
// 捕獲異常:類似 catch(\Exception $e)
if err := recover(); err != nil {
// 輸出異常
fmt.Println(err)
// 打印錯誤堆棧: 類似于PHP的 debug_print_backtrace
debug.PrintStack() // 輸出到日志即可
fmt.Println()
fmt.Println("執(zhí)行:planB!")
planB()
}
}()
// 注意??!用recover處理panic指令,defer 須在 panic 之前聲明,否則panic時會直接退出,recover無法捕獲到panic
tryCatchFinally()
fmt.Println("dadada...") // 這里不會執(zhí)行
}
func tryCatchFinally() {
fmt.Println("begin")
panic("拋出異常信息: throw new exception")
fmt.Println("end") // 這里不會執(zhí)行
}
func planB() {
fmt.Println("exec-planB, done!!")
}關(guān)于內(nèi)建函數(shù) recover
1、用來控制一個 goroutine 的 panicking 行為,捕獲panic,從而影響應用的行為
2、一般調(diào)用方式
- 在defer函數(shù)中,通過 recover 來終止一個 gojroutine 的 panicking 過程,從而恢復正常邏輯的執(zhí)行
- 獲取通過panic傳遞的error,執(zhí)行捕獲之后的邏輯,參考傳統(tǒng)tryCatchFinally
EOF - 錯誤處理
另一種常見錯誤處理,即 EOF(End-Of-File)文件結(jié)尾錯誤,可以直接捕獲,示例如下:
reader := strings.NewReader("clear is better than clever")
p := make([]byte, 5)
for {
n, err := reader.Read(p)
if err != nil {
if err == io.EOF {
fmt.Println("EOF:", n)
} else {
fmt.Println(err)
}
os.Exit(1)
}
// 注: p 每次循環(huán)都會被覆蓋,但如果最后一行不足5個字節(jié)時:
// 僅會覆蓋最后一行獲取到的相應的字節(jié)數(shù),比如最后一行3字節(jié),僅覆蓋p的前3個,第4、5個字節(jié)維持上次循環(huán)時的賦值
// 所以這里輸出時使用了:p[:n]
fmt.Println(n, string(p[:n]))
}如上執(zhí)行結(jié)果:
5 clear
5 is b
5 etter
5 than
5 clev
2 erlev
EOF: 0
exit status 1
以上就是golang之資源釋放/異常錯誤處理解析的詳細內(nèi)容,更多關(guān)于golang資源釋放異常錯誤處理的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go利用Swag實現(xiàn)將注釋轉(zhuǎn)換為專業(yè)的API文檔
Swag是一個將Go語言注釋自動轉(zhuǎn)換為Swagger 2.0文檔的工具,支持多種流行Go Web框架,本文我們就來簡單講講如何使用Swag將簡單的代碼注釋生成專業(yè)的API文檔的吧2025-06-06
go動態(tài)限制并發(fā)數(shù)量的實現(xiàn)示例
本文主要介紹了Go并發(fā)控制方法,通過帶緩沖通道和第三方庫實現(xiàn)并發(fā)數(shù)量限制,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2025-07-07

