中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

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

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

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

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

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

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

問題定義

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

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

根據這道題的題意,可大致理解其想要問的是 Go 調度相關的一些知識理解。

單核 CPU

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

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

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

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

這三者交互實際來源于 Go 的 M: N 調度模型。也就是 M 必須與 P 進行綁定,然后不斷地在 M 上循環尋找可運行的 G 來執行相應的任務。

Goroutine 受限

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

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

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

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

 

Go 版本的問題

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

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

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

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

 

實戰演練

在剛剛過去的 3s 中,你已經把上面的考量都在大腦中過了一遍。接下來我們正式進入實戰演練,構造一個例子:

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

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

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

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

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

答案是:

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

為什么

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

 

不會輸出任何結果

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

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

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

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

 

正常輸出結果

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

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

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

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

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

綁定相應的 runtime.doSigPreempt 搶占方法:

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

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

  • 搶占阻塞在系統調用上的 P。
  • 搶占運行時間過長的 G。

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

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

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

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

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

向AI問一下細節

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

AI

吉木乃县| 丰镇市| 太湖县| 望城县| 阿鲁科尔沁旗| 凯里市| 昭平县| 灵山县| 兴隆县| 夏河县| 伊吾县| 昂仁县| 安平县| 涟源市| 台北县| 清河县| 东阿县| 博白县| 宜阳县| 曲靖市| 华阴市| 赤水市| 米脂县| 乐山市| 汝南县| 右玉县| 海晏县| 商丘市| 定襄县| 绵阳市| 梅河口市| 施秉县| 盐山县| 凤冈县| 泊头市| 犍为县| 家居| 新干县| 若尔盖县| 荃湾区| 富蕴县|