Goroutine 一泄露就看到他,這是個什麼?

大家好,我是煎魚。

作爲一個 Go 語言的使用大戶,常常就有人冷不丁的,一下就泄露了... 泄露了啥。煎魚幫我看看?

表象來看當然是 goroutine 泄露了,這時候就會有小夥伴開始跑去拉取 PProf。就會看到類似下面這張圖:

重點會看到 runtime.gopark 這個函數,在所有的 goroutine 泄露中都會看到有,並且都會是大頭。

既然是大頭,也就會有許多朋友以爲他是泄漏點,在那一頓猛查,那這個函數到底是什麼,作用是?

runtime.gopark 是何物

想要知道 runtime.gopark 函數是作用,最快的辦法就是看源碼了。其實現細節在 src/runtime/proc.go 文件中。

源代碼如下:

func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason waitReason, traceEv byte, traceskip int) {
 mp := acquirem()
 gp := mp.curg
 status := readgstatus(gp)
 mp.waitlock = lock
 mp.waitunlockf = unlockf
 gp.waitreason = reason
 mp.waittraceev = traceEv
 mp.waittraceskip = traceskip
 releasem(mp)
 
 mcall(park_m)
}

該函數主要作用有三大點:

熟讀了其源碼後,我們可得知該函數的關鍵作用就是將當前的 goroutine 放入等待狀態,這意味着 goroutine 被暫時被擱置了,也就是被運行時調度器暫停了。

緣由

回到最初的問題,之所以 goroutine 泄露,你就會看到大量的 runtime.gopark 函數,這是因爲 goroutine 泄露一般不會單單只是一個 goroutine,肯定是會有多個的。

同時這些 goroutine 在調用了 runtime.gopark 函數後都被暫停了,也就是進入休眠狀態,自然而然也就停留在此。

直至滿足條件後再被 runtime.goready 函數喚醒,該函數會將已準備就緒的 goroutine 切換狀態,再加入運行隊列,等待調度器的新一輪調度。

思考

前幾天就有讀者在我的 Go 讀者羣(可以加我後拉你進羣)中諮詢了下述問題,也和  runtime.gopark 函數有關。問題如下:

經過上述的分析,顯然 runtime.gopark 不是 goroutine 的一種狀態,導致 goroutine 狀態變更只是他的執行過程中所涉及到,產生的一個結果。

而 goroutine 的狀態一共有 9 種,有興趣的小夥伴可以瞭解。如下:

3CLnQs

總結

在今天這篇文章中,我們介紹了大家最常碰到的 goroutine 泄露,而在泄露後最關心的 runtime.gopark 函數的意義,我們從源碼再到作用進行了一輪剖析。

下次如果再有人問你 runtime.gopark 是幹嘛用的,就可以愉快的把這篇文章甩給他,分享你的知識啦 :)

關注煎魚,吸取他的知識 👆

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/W4dmhdjbiRH0KWSDdpXikQ