13. Go 語言異常機制:panic 和 recover

Hi,你們好,我是明哥。git

在本身學習 Golang 的這段時間裏,我寫了詳細的學習筆記放在個人我的微信公衆號 《Go編程時光》,對於 Go 語言,我也算是個初學者,所以寫的東西應該會比較適合剛接觸的同窗,若是你也是剛學習 Go 語言,不防關注一下,一塊兒學習,一塊兒成長。github

個人在線博客:http://golang.iswbm.com
個人 Github:github.com/iswbm/GolangCodingTimegolang


編程語言通常都會有異常捕獲機制,在 Python 中 是使用raisetry-except 語句來實現的異常拋出和異常捕獲的。web

在 Golang 中,有很多常規錯誤,在編譯階段就能提早告警,好比語法錯誤或類型錯誤等,可是有些錯誤僅能在程序運行後才能發生,好比數組訪問越界、空指針引用等,這些運行時錯誤會引發程序退出。shell

固然能觸發程序宕機退出的,也能夠是咱們本身,好比通過檢查判斷,當前環境沒法達到咱們程序進行的預期條件時(好比一個服務指定監聽端口被其餘程序佔用),能夠手動觸發 panic,讓程序退出中止運行。編程

1. 觸發panic

手動觸發宕機,是很是簡單的一件事,只須要調用 panic 這個內置函數便可,就像這樣子數組

package main

func main() {
	panic("crash")
}

運行後,直接報錯宕機服務器

$ 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

發生了異常,有時候就得捕獲,就像 Python 中的 except 同樣,那 Golang 中是如何作到的呢?微信

這就不得不引出另一個內建函數 -- recover,它可讓程序在發生宕機後起生回生。編程語言

可是 recover 的使用,有一個條件,就是它必須在 defer 函數中才能生效,其餘做用域下,它是不工做的。

這是一個簡單的例子

import "fmt"

func set_data(x int) {
	defer func() {
		// recover() 能夠將捕獲到的panic信息打印
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	// 故意製造數組越界,觸發 panic
	var arr [10]int
	arr[x] = 88
}

func main() {
	set_data(20)

	// 若是能執行到這句,說明panic被捕獲了
	// 後續的程序能繼續運行
	fmt.Println("everything is ok")
}

運行後,輸出以下

$ go run main.go
runtime error: index out of range [20] with length 10
everything is ok

一般來講,不該該對進入 panic 宕機的程序作任何處理,但有時,須要咱們能夠從宕機中恢復,至少咱們能夠在程序崩潰前,作一些操做,舉個例子,當 web 服務器遇到不可預料的嚴重問題時,在崩潰前應該將全部的鏈接關閉,若是不作任何處理,會使得客戶端一直處於等待狀態,若是 web 服務器還在開發階段,服務器甚至能夠將異常信息反饋到客戶端,幫助調試。

3. 沒法跨協程

從上面的例子,能夠看到,即便 panic 會致使整個程序退出,但在退出前,如有 defer 延遲函數,仍是得執行完 defer 。

可是這個 defer 在多個協程之間是沒有效果,在子協程裏觸發 panic,只能觸發本身協程內的 defer,而不能調用 main 協程裏的 defer 函數的。

來作個實驗就知道了

import (
	"fmt"
	"time"
)

func main() {
    // 這個 defer 並不會執行
	defer fmt.Println("in main")
    
	go func() {
		defer println("in goroutine")
		panic("")
	}()

	time.Sleep(2 * time.Second)
}

輸出以下

in goroutine
panic:

goroutine 6 [running]:
main.main.func1()
        E:/Go-Code/main.go:12 +0x7b
created by main.main
        E:/Go-Code/main.go:10 +0xbc
exit status 2

4. 總結一下

Golang 異常的拋出與捕獲,依賴兩個內置函數:

  • panic:拋出異常,使程序崩潰
  • recover:捕獲異常,恢復程序或作收尾工做

revocer 調用後,拋出的 panic 將會在此處終結,不會再外拋,可是 recover,並不能任意使用,它有強制要求,必須得在 defer 下才能發揮用途。

系列導讀

01. 開發環境的搭建(Goland & VS Code)

02. 學習五種變量建立的方法

03. 詳解數據類型:****整形與浮點型

04. 詳解數據類型:byte、rune與string

05. 詳解數據類型:數組與切片

06. 詳解數據類型:字典與布爾類型

07. 詳解數據類型:指針

08. 面向對象編程:結構體與繼承

09. 一篇文章理解 Go 裏的函數

10. Go語言流程控制:if-else 條件語句

11. Go語言流程控制:switch-case 選擇語句

12. Go語言流程控制:for 循環語句

13. Go語言流程控制:goto 無條件跳轉

14. Go語言流程控制:defer 延遲調用

15. 面向對象編程:接口與多態

16. 關鍵字:make 和 new 的區別?

17. 一篇文章理解 Go 裏的語句塊與做用域

18. 學習 Go 協程:goroutine

19. 學習 Go 協程:詳解信道/通道

20. 幾個信道死鎖經典錯誤案例詳解

21. 學習 Go 協程:WaitGroup

22. 學習 Go 協程:互斥鎖和讀寫鎖

23. Go 裏的異常處理:panic 和 recover

24. 超詳細解讀 Go Modules 前世此生及入門使用

25. Go 語言中關於包導入必學的 8 個知識點

26. 如何開源本身寫的模塊給別人用?

27. 說說 Go 語言中的類型斷言?

28. 這五點帶你理解Go語言的select用法


相關文章
相關標籤/搜索