溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Go語言中單核CPU開兩個Goroutine時其中一個死循環(huán)會怎么樣

發(fā)布時間:2021-09-18 11:38:06 來源:億速云 閱讀:167 作者:柒染 欄目:編程語言

本篇文章為大家展示了Go語言中單核CPU開兩個Goroutine時其中一個死循環(huán)會怎么樣,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

今天的男主角,是與 Go 工程師有調(diào)度相關(guān)的知識,那就是 “單核 CPU,開兩個 Goroutine,其中一個死循環(huán),會怎么樣?

請在此處默念自己心目中的答案,再往和煎魚一起研討一波 Go 的技術(shù)哲學。

問題定義

針對這個問題,我們需要把問題剖開來看看,其具有以下幾個元素:

  • 運行 Go 程序的計算機只有一個單核 CPU。
  • 兩個 Goroutine 在運行。
  • 一個 Goroutine 死循環(huán)。

根據(jù)這道題的題意,可大致理解其想要問的是 Go 調(diào)度相關(guān)的一些知識理解。

單核 CPU

第一個要點,就是要明確 “計算機只有一個單核 CPU” 這一個變量定義,對 Go 程序會產(chǎn)生什么影響,否則很難繼續(xù)展開。

既然明確涉及 Goroutine,這里就會考察到你對 Go 的調(diào)度模型 GMP 的基本理解了。

從單核 CPU 來看,最大的影響就是 GMP 模型中的 P,因為 P 的數(shù)量默認是與 CPU 核數(shù)(GOMAXPROCS)保持一致的。

  • G:Goroutine,實際上我們每次調(diào)用     go func 就是生成了一個 G。
  • P:Processor,處理器,一般 P 的數(shù)量就是處理器的核數(shù),可以通過     GOMAXPROCS 進行修改。
  • M:Machine,系統(tǒng)線程。

這三者交互實際來源于 Go 的 M: N 調(diào)度模型。也就是 M 必須與 P 進行綁定,然后不斷地在 M 上循環(huán)尋找可運行的 G 來執(zhí)行相應(yīng)的任務(wù)。

Goroutine 受限

第二個要點,就是 Goroutine 的數(shù)量和運行模式都是受限的。有兩個 Goroutine,一個 Goroutine 在死循環(huán),另外一個在正常運行。

這可以理解為 Main Goroutine + 起了一個新 Goroutine 跑著死循環(huán),因為本身 main 函數(shù)就是一個主協(xié)程在運行著,沒毛病。

需要注意的是,Goroutine 里跑著死循環(huán),也就是時時刻刻在運行著 “業(yè)務(wù)邏輯”。這塊需要與單核 CPU 關(guān)聯(lián)起來,考慮是否會一直阻塞住,把整個 Go 進程運行給 hang 住了

注:但若是在現(xiàn)場面試,可以先枚舉出這種場景,詮釋清楚后。再補充提問面試官,是否這類場景?

 

Go 版本的問題

第三個要點,是一個隱性的拓展點。如果你是一個老 Go 粉,經(jīng)常關(guān)注 Go 版本的更新情況(至少大版本),則應(yīng)該會知道 Go 的調(diào)度是會變動的(會在后面的小節(jié)講解)。

因此本文這個問題,在不同的 Go 語言版本中,結(jié)果可能會是不一樣的。但是面試官并沒有指出,這里就需要考慮到:

  1. 面試官故意不指出,等著你指出。
  2. 面試官沒留意到這塊,沒想那么多。
  3. 面試官自己都不知道這塊的 “新” 知識,他的知識可能還是老的。

如果你注意到了,是一個小亮點,說明你在這塊有一定的知識積累。

 

實戰(zhàn)演練

在剛剛過去的 3s 中,你已經(jīng)把上面的考量都在大腦中過了一遍。接下來我們正式進入實戰(zhàn)演練,構(gòu)造一個例子:

// Main Goroutine 
func main() {
    // 模擬單核 CPU
    runtime.GOMAXPROCS(1)
    
    // 模擬 Goroutine 死循環(huán)
    go func() {
        for {
        }
    }()

    time.Sleep(time.Millisecond)
    fmt.Println("腦子進煎魚了")
}
 

在上面的例子中,我們通過以下方式達到了面試題所需的目的:

  • 設(shè)置     runtime.GOMAXPROCS 方法模擬了單核 CPU 下只有一個 P 的場景。
  • 運行一個 Goroutine,內(nèi)部跑一個 for 死循環(huán),達到阻塞運行的目的。
  • 運行一個 Goroutine,主函數(shù)(main)本身就是一個 Main Goroutine。

思考一下:這段程序是否會輸出 ”腦子進煎魚了“ 呢,為什么?

答案是:

  • 在 Go1.14 前,不會輸出任何結(jié)果。
  • 在 Go1.14 及之后,能夠正常輸出結(jié)果。
 

為什么

這是怎么回事呢,這兩種情況分別對應(yīng)了什么原因和標準,Go 版本的變更有帶來了什么影響?

 

不會輸出任何結(jié)果

顯然,這段程序是有一個 Goroutine 是正在執(zhí)行死循環(huán),也就是說他肯定無法被搶占。

這段程序中更沒有涉及主動放棄執(zhí)行權(quán)的調(diào)用(runtime.Gosched),又或是其他調(diào)用(可能會導(dǎo)致執(zhí)行權(quán)轉(zhuǎn)移)的行為。因此這個 Goroutine 是沒機會溜號的,只能一直打工...

那為什么主協(xié)程(Main Goroutine)會無法運行呢,其實原因是會優(yōu)先調(diào)用休眠,但由于單核 CPU,其只有唯一的 P。唯一的 P 又一直在打工不愿意下班(執(zhí)行 for 死循環(huán),被迫無限加班)。

因此主協(xié)程永遠沒有機會唄調(diào)度,所以這個 Go 程序自然也就一直阻塞在了執(zhí)行死循環(huán)的 Goroutine 中,永遠無法下班(執(zhí)行完畢,退出程序)。

 

正常輸出結(jié)果

那為什么 Go1.14 及以后的版本,又能正常輸出了呢?

主要還是在 Go1.14 實現(xiàn)了基于信號的搶占式調(diào)度,以此來解決上述一些仍然無法被搶占解決的場景。

主要原理是Go 程序在啟動時,會在 runtime.sighandler 方法注冊并且綁定 SIGURG 信號:

func mstartm0() {
 ...
 initsig(false)
}

func initsig(preinit bool) {
 for i := uint32(0); i < _NSIG; i++ {
  ...
  setsig(i, funcPC(sighandler))
 }
}
 

綁定相應(yīng)的 runtime.doSigPreempt 搶占方法:

func sighandler(sig uint32, info *siginfo, ctxt unsafe.Pointer, gp *g) {
    ...
    if sig == sigPreempt && debug.asyncpreemptoff == 0 {
        // 執(zhí)行搶占
        doSigPreempt(gp, c)
    }
}
 

同時在調(diào)度的 runtime.sysmon 方法會調(diào)用 retake 方法處理一下兩種場景:

  • 搶占阻塞在系統(tǒng)調(diào)用上的 P。
  • 搶占運行時間過長的 G。

該方法會檢測符合場景的 P,當滿足上述兩個場景之一時,就會發(fā)送信號給 M。M 收到信號后將會休眠正在阻塞的 Goroutine,調(diào)用綁定的信號方法,并進行重新調(diào)度。以此來解決這個問題。

注:在 Go 語言中,sysmon 會用于檢測搶占。sysmon 是 Go 的 Runtime 的系統(tǒng)檢測器,sysmon 可進行 forcegc、netpoll、retake 等一系列騷操作(via @xiaorui)。

在這篇文章中,我們針對 ”單核 CPU,開兩個 Goroutine,其中一個死循環(huán),會怎么樣?“ 這個問題進行了展開剖析。

針對不同 Go 語言版本,不同程序邏輯的表現(xiàn)形式都不同,但背后的基本原理都是與 Go 調(diào)度模型和搶占有關(guān)。

上述內(nèi)容就是Go語言中單核CPU開兩個Goroutine時其中一個死循環(huán)會怎么樣,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI