您好,登錄后才能下訂單哦!
本篇內容介紹了“Go語言調度的本質是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
首先拋出本文的結論:Go 調度的本質是一個生產-消費流程。
生產者-消費者
我們平時用 Go 最爽的一點莫過于用一句 go func(){}() 就啟動了一個 goroutine 來并發地執行任務。這比用 C/C++ 啟動一個線程并發地去執行任務方便太多。這句代碼實際上就生產出了一個 goroutine,并進入可運行隊列,等待和 m 來找它從而可以得到運行。
熟悉 GMP 模型的朋友都知道,goroutine 最終在 m 上得以執行,因為操作系統感知不到 goroutine,它只能感知線程,并且線程可以看成是 m。
所以,m 拿到 goroutine 并運行它的過程就是一個消費過程。
生產-消費過程
生產出的 goroutine 需要找一個地方存放,這個地方就是可運行隊列。在 Go 程序中,可運行隊列是分級的,分為三級:
三級可運行隊列
runnext 實際上只能指向一個 goroutine,所以它是一個特殊的隊列。
那把 goroutine 放到哪個可運行隊列呢?看情況。
首先,如果 runnext 為空,那么 goroutine 就會順利地放入 runnext,接下來,它會以最高優先級得到運行,即優先被消費。
如果 runnext 不為空,那就先負責把 runnext 上的 old goroutine 踢走,再把 new goroutine 放上來。具體踢到哪里呢?又得分情況。
local queue 是一個大小為 256 的數組,實際上用 head 和 tail 指針把它當成一個環形數組在使用。如果 local queue 不滿,則將 runnext 放入 local queue;否則,P 的本地隊列上的 goroutine 太多了,說明當前 P 的任務太重了,需要減負,因此需要得到其他 P 協助。從而,將 runnext 以及當前 P 的一半 goroutine 一起打包丟到 global queue 里去。
當然,這部分課程里有非常生動的動畫,這里貼一個截圖大家感受一下:
生產者動畫
之前的文章里也講到過調度循環是咋回事,它實際上就是 Go 程序在啟動的時候,會創建和 CPU 核心數相等個數的 P,會創建初始的 m,稱為 m0。這個 m0 會啟動一個調度循環:不斷地找 g,執行,再找 g……
偽代碼是這樣的:
調度循環
隨著程序的運行,m 更多地被創建出來,因此會有更多的調度循環在執行。
那邊生產者在不斷地生產 g,這邊 m 的調度循環不斷地在消費 g,整個過程就 run 起來了。
找 g 的過程中當然也是從上面的三級隊列里找:
先看 runnext,再看 local queue,再看 global queue。當然,如果實在找不到,就去其他 p 去偷。
“Go語言調度的本質是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。