偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Go 語言中 panic 和 recover 搭配使用

開發(fā) 前端
當(dāng)我們?cè)谕粋€(gè)協(xié)程中出現(xiàn)了 panic,且在同一個(gè)協(xié)程中去使用 defer 來配合 recover 來進(jìn)行捕獲異常和處理異常,就可以得以實(shí)現(xiàn),看到這里,有沒有覺得還是蠻簡(jiǎn)單的,不就是去對(duì)一個(gè) p.recovered 進(jìn)行配合處理嗎?

本次主要聊聊 Go 語言中關(guān)于 panic 和 recover 搭配使用 ,以及 panic 的基本原理

最近工作中審查代碼的時(shí)候發(fā)現(xiàn)一段代碼,類似于如下這樣,將 recover 放到一個(gè)子協(xié)程里面,期望去捕獲主協(xié)程的程序異常

圖片圖片

看到此處,是否會(huì)想這段代碼在項(xiàng)目中是想當(dāng)然寫出來的吧,然而平日中,大多問題是出現(xiàn)在認(rèn)知偏差上,那么本次,我們就來消除一下這個(gè)認(rèn)知偏差

關(guān)于 Go 語言中顯示的使用 panic 的地方不多,一般 panic ,基本上會(huì)出現(xiàn)在咱們程序出現(xiàn)異常退出的時(shí)候

例如訪問了空指針里面的值,則會(huì) panic 報(bào)錯(cuò)無效的內(nèi)存地址,又例如訪問量數(shù)組中不存在的數(shù)組所索引,或者切片索引,那么會(huì)報(bào)錯(cuò) panic 數(shù)組越界等等

可是碰到這些 panic 的時(shí)候,實(shí)際上我們并不期望當(dāng)前的服務(wù)直接掛掉,而是期望這個(gè)異常能夠被識(shí)別,且不影響程序其他部分的模塊運(yùn)行

正常捕獲異常

在 Go 中可以將 defer 和 recover 進(jìn)行搭配使用,可以捕獲和處理大部分的異常情況,例如可以這樣

圖片圖片

這里可以看到,recover 捕獲異常和發(fā)生異常的部分是在同一個(gè)協(xié)程中,實(shí)驗(yàn)證明是可以正常捕獲并且處理異常

并沒有捕獲到異常

  1. 直接不做顯示的 recover,自然 panic 程序崩潰會(huì)如期而至,此處我們顯示的使用 panic 函數(shù)來制造恐慌
func main() {
   log.SetFlags(log.Lshortfile)

   panic("panic coming...")

}

圖片圖片

  1. 不使用 defer 來進(jìn)行處理
func main() {
   log.SetFlags(log.Lshortfile)
    if err := recover(); err != nil {
     log.Println("recover panic : ", err)
    }
   panic("panic coming...")

}

圖片圖片

自然 recover 函數(shù)是在 panic 調(diào)用之前就已經(jīng)執(zhí)行,此時(shí)是還沒有異常需要捕獲和恢復(fù)的,待程序運(yùn)行到 panic 處的時(shí)候,實(shí)際上并沒有沒有處理程序崩潰的異常

結(jié)果,仍然是程序崩潰

  1. 當(dāng)然,還有文章開頭提到的出現(xiàn) panic 的位置和捕獲和處理程序崩潰異常的位置不在同一個(gè)協(xié)程,自然也是沒法捕獲到的,這一點(diǎn)需要注意,其他的語言可能不是這樣,但是 Go 中是這樣的

panic 基本原理

看了上述現(xiàn)象,實(shí)際上還是對(duì)知識(shí)點(diǎn)理解得不夠,使用的時(shí)候想當(dāng)然了,就像使用 defer 一樣,如果對(duì)他不夠了解的話,使用的時(shí)候,確實(shí)會(huì)出現(xiàn)一些奇奇怪怪的現(xiàn)象,對(duì)于 defer 的使用可以查看文末的文章地址

  1. panic 函數(shù)和 recover 函數(shù),Go 源碼builtin\builtin.go中可以看到注釋

圖片圖片

注釋中有說關(guān)于 panic 和 recover 的使用是作用于當(dāng)前協(xié)程的,因此我們使用的時(shí)候,如果跨協(xié)程教程使用,自然不會(huì)達(dá)到我們期望的效果

  1. 繼續(xù)查看關(guān)于 panic 的源碼,實(shí)際上是一個(gè)結(jié)構(gòu),放到 defer 結(jié)構(gòu)里面的一個(gè)指針,源碼位置:runtime\runtime2.go

圖片圖片

_panic 的結(jié)構(gòu)如下:

type _panic struct {
   argp      unsafe.Pointer
   arg       interface{}
   link      *_panic
   pc        uintptr
   sp        unsafe.Pointer
   recovered bool
   aborted   bool
   goexit    bool
}

上述兩個(gè)結(jié)構(gòu)表達(dá)的意思是,程序中出現(xiàn) panic 的時(shí)候,實(shí)際上都會(huì)創(chuàng)建一個(gè) _panic 結(jié)構(gòu),這個(gè) _panic 結(jié)構(gòu)里面存儲(chǔ)了當(dāng)前程序崩潰的一些必要信息,如下:

  1. argp

是一個(gè) unsafe.Pointer 類型的成員,指向 defer 調(diào)用參數(shù)的指針

  1. arg

出現(xiàn) panic 的原因,如果我們顯示調(diào)用 panic,那么就是我們填入 panic 函數(shù)中的參數(shù),例如上述的 panic coming ...

  1. link

是一個(gè)指針,指向上一個(gè),最近的一個(gè) _panic 結(jié)構(gòu)的地址,實(shí)際上此處就可以看到這個(gè)指針對(duì)應(yīng)的是一個(gè)鏈表,一個(gè)又多個(gè) _panic 結(jié)構(gòu)組成的鏈表

圖片圖片

  1. recovered

panic 是否已經(jīng)處理完畢,即當(dāng)前的這個(gè) panic 是否是已經(jīng)被 recover 了

  1. aborted

表示當(dāng)前的 panic 是否被中止

  1. 對(duì)于 pc 和 sp 自然就是我們熟知的 pc 通用寄存器,在匯編中是指向當(dāng)前運(yùn)行指令的下一條指令,sp 則是棧指針 stack pointer,用于入棧和出棧的

我們知道運(yùn)行函數(shù)的時(shí)候需要入棧,運(yùn)行完畢之后需要出棧

源碼中的 runtime.gopanic

那么我們繼續(xù)來閱讀源碼,上述看到 sp 和 pc ,那么我們就簡(jiǎn)單寫一個(gè) panic 的代碼來看看匯編到底是怎么執(zhí)行的,不用擔(dān)心看不懂,我們只需要看關(guān)鍵詞就行

還是上面的程序

圖片圖片

程序運(yùn)行的時(shí)候可以執(zhí)行 go tool compile -S main.go

可以看到匯編代碼,可能其他的看不懂,但是我們可以看到如下關(guān)鍵詞

圖片圖片

  • log.(*Logger).SetFlags(SB) 即是執(zhí)行到我們調(diào)用 log 去設(shè)置參數(shù)
  • 程序走到 panic 函數(shù)的時(shí)候,實(shí)際上是執(zhí)行了 runtime.gopanic 函數(shù),我們一起看看源碼

圖片圖片

代碼中可以看到 p.recovered 邏輯下的關(guān)于 recover 的邏輯被刪除掉了,在文章的后面會(huì)繼續(xù)說到,當(dāng)前我們先關(guān)注 panic 的事項(xiàng)

runtime.gopanic 程序的邏輯大體是這樣的

  1. 獲取當(dāng)前 協(xié)程 的指針
  2. 初始化一個(gè) _panic 結(jié)構(gòu) p,并將當(dāng)前協(xié)程上對(duì)應(yīng)的數(shù)據(jù)賦值給到 p 上,且將 當(dāng)前協(xié)程 _panic 掛到 link 上
  3. 進(jìn)入循環(huán)后,拿到當(dāng)前協(xié)程的 _defer 數(shù)據(jù)
  4. 查看 _defer 指針數(shù)據(jù) 中是否有 defer 調(diào)用,如果有則執(zhí)行
  5. 處理完基本邏輯之后,打印 panic 信息,例如我們 demo 中的 panic coming ... 信息
  6. 最終退出程序

Xdm 可以看上圖,自己捋一捋邏輯就清晰了

接著,我們來看

fatalpanic

圖片圖片

通過 runtime.gopanic 我們可以看到 fatalpanic 函數(shù)基本上就是做一個(gè)收尾工作了,如果上述程序處理完畢之后, fatalpanic 校驗(yàn)到 panic 是需要 recover 的,那么就打印 [recovered]

打印的這個(gè)信息是由 上圖中 printpanics 完成的

圖片圖片

這下知道 panic 是如何去執(zhí)行的了,那么對(duì)于現(xiàn)在來研究 recover 是如何落實(shí)的

recover

還是同一個(gè)例子,咱們將 defer 部分的代碼注打開,來繼續(xù)看看效果

func main() {
   log.SetFlags(log.Lshortfile)

   defer func() {
      if err := recover(); err != nil {
         log.Println("recover panic : ", err)
      }
   }()

   panic("panic coming...")

}

自然效果是我們期望的,捕獲到了異常,且處理了

圖片圖片

繼續(xù)打印匯編來查看一下關(guān)鍵詞,是否有我們期望的函數(shù)出現(xiàn)

圖片圖片

圖片圖片

此處我們可以看到,實(shí)際 Go 中調(diào)用了多個(gè)函數(shù)

  1. runtime.gorecover
  2. main.main.opendefer
  3. log.(*Logger).SetFlags
  4. runtime.gopanic
  5. runtime.deferreturn

自然明眼人都看的出現(xiàn),關(guān)鍵的函數(shù)實(shí)現(xiàn)自然是 runtime.gorecover ,那么我們來一探究竟

runtime.gorecover

圖片圖片

查看源碼我們可以知道, runtime.gorecover 實(shí)際上就是根據(jù)當(dāng)前協(xié)程的 _panic 結(jié)構(gòu)數(shù)據(jù)來判斷是否需要恢復(fù),如果需要?jiǎng)t將 p.recovered = true

自然在這里將當(dāng)前協(xié)程的數(shù)據(jù)修改掉,正是為了后續(xù)執(zhí)行 runtime.gopanic 的時(shí)候提供保障, runtime.gopanic 執(zhí)行的時(shí)候就會(huì)去判斷和處理這個(gè) p.recovered

前文中提到的關(guān)于 runtime.gopanic 中 處理 p.recovered 的邏輯是這樣的

圖片圖片

圖片圖片

  1. 如上可以看到 runtime.gorecover 去對(duì) p.recovered 設(shè)置是否恢復(fù)
  2. runtime.gopanic 中校驗(yàn) p.recovered 已處理,則執(zhí)行 recovery 函數(shù)
  3. recovery 函數(shù)中去處理對(duì)應(yīng)的寄存器的值去維護(hù)上下文
  4. 最后我們可以看到最終調(diào)用 gogo 函數(shù)跳回原來調(diào)用的位置

因此,當(dāng)我們?cè)谕粋€(gè)協(xié)程中出現(xiàn)了 panic,且在同一個(gè)協(xié)程中去使用 defer 來配合 recover 來進(jìn)行捕獲異常和處理異常,就可以得以實(shí)現(xiàn),看到這里,有沒有覺得還是蠻簡(jiǎn)單的,不就是去對(duì)一個(gè) p.recovered 進(jìn)行配合處理嗎

自然,表面上是這樣,其中對(duì)于寄存器的各種數(shù)據(jù)處理涉及的內(nèi)容還是不少的,不過這不在我們今天聊的范疇中了

總結(jié)

至此,相信你已經(jīng)知道了這些

  1. 為什么 panic 和 defer ,recover 配合使用的時(shí)候要在同一個(gè)協(xié)程中了吧
  2. 相信你還知道了 panic 和 recover 的處理流程
責(zé)任編輯:武曉燕 來源: 阿兵云原生
相關(guān)推薦

2013-06-25 09:52:32

GoGo語言Go編程

2025-02-06 13:19:31

RustPin系統(tǒng)

2025-06-09 01:15:00

2024-04-01 00:02:56

Go語言代碼

2024-05-10 08:36:40

Go語言對(duì)象

2012-06-15 09:56:40

2023-12-30 18:35:37

Go識(shí)別應(yīng)用程序

2023-11-21 15:46:13

Go內(nèi)存泄漏

2023-04-09 23:09:59

Go語言函數(shù)

2023-05-06 09:36:38

RecoverPanic

2024-01-07 23:11:16

defer?Go語言

2014-04-09 09:32:24

Go并發(fā)

2021-09-27 23:28:29

Go多協(xié)程并發(fā)

2025-03-31 08:57:25

Go程序性能

2024-04-07 11:33:02

Go逃逸分析

2021-07-15 23:18:48

Go語言并發(fā)

2023-12-21 07:09:32

Go語言任務(wù)

2023-01-12 08:52:50

GoroutinesGo語言

2021-12-31 09:28:46

小字端大字端Go

2025-03-31 00:29:44

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)