您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)Golang協(xié)程調(diào)度的詳細(xì)分析,小編覺得挺實(shí)用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
下面看看golang的協(xié)程調(diào)度
groutine能擁有強(qiáng)大的并發(fā)實(shí)現(xiàn)是通過GPM調(diào)度模型實(shí)現(xiàn),下面就來解釋下goroutine的調(diào)度模型。
Go的調(diào)度器內(nèi)部有三個重要的結(jié)構(gòu):M,P,G
M:M是對內(nèi)核級線程的封裝,數(shù)量對應(yīng)真實(shí)的CPU數(shù),一個M就是一個線程,goroutine就是跑在M之上的;M是一個很大的結(jié)構(gòu),里面維護(hù)小對象內(nèi)存cache(mcache)、當(dāng)前執(zhí)行的goroutine、隨機(jī)數(shù)發(fā)生器等等非常多的信息
G:代表一個goroutine,它有自己的棧,instruction pointer和其他信息(正在等待的channel等等),用于調(diào)度。
P:P全稱是Processor,處理器,它的主要用途就是用來執(zhí)行g(shù)oroutine的。每個Processor對象都擁有一個LRQ(Local Run Queue),未分配的Goroutine對象保存在GRQ(Global Run Queue )中,等待分配給某一個P的LRQ中,每個LRQ里面包含若干個用戶創(chuàng)建的Goroutine對象。
Golang采用的是多線程模型,更詳細(xì)的說他是一個兩級線程模型,但它對系統(tǒng)線程(內(nèi)核級線程)進(jìn)行了封裝,暴露了一個輕量級的協(xié)程goroutine(用戶級線程)供用戶使用,而用戶級線程到內(nèi)核級線程的調(diào)度由golang的runtime負(fù)責(zé),調(diào)度邏輯對外透明。goroutine的優(yōu)勢在于上下文切換在完全用戶態(tài)進(jìn)行,無需像線程一樣頻繁在用戶態(tài)與內(nèi)核態(tài)之間切換,節(jié)約了資源消耗。
從上圖中看,有2個物理線程M,每一個M都擁有一個處理器P,每一個也都有一個正在運(yùn)行的goroutine。
P的數(shù)量可以通過GOMAXPROCS()來設(shè)置,它其實(shí)也就代表了真正的并發(fā)度,即有多少個goroutine可以同時運(yùn)行。
圖中灰色的那些goroutine并沒有運(yùn)行,而是出于ready的就緒態(tài),正在等待被調(diào)度。P維護(hù)著這個隊(duì)列(稱之為runqueue),
Go語言里,啟動一個goroutine很容易:go function 就行,所以每有一個go語句被執(zhí)行,runqueue隊(duì)列就在其末尾加入一個
goroutine,在下一個調(diào)度點(diǎn),就從runqueue中取出(如何決定取哪個goroutine?)一個goroutine執(zhí)行。
當(dāng)一個OS線程M0陷入阻塞時(如下圖),P轉(zhuǎn)而在運(yùn)行M1,圖中的M1可能是正被創(chuàng)建,或者從線程緩存中取出。
當(dāng)MO返回時,它必須嘗試取得一個P來運(yùn)行g(shù)oroutine,一般情況下,它會從其他的OS線程那里拿一個P過來,
如果沒有拿到的話,它就把goroutine放在一個global
runqueue里,然后自己睡眠(放入線程緩存里)。所有的P也會周期性的檢查global
runqueue并運(yùn)行其中的goroutine,否則global runqueue上的goroutine永遠(yuǎn)無法執(zhí)行。
另一種情況是P所分配的任務(wù)G很快就執(zhí)行完了(分配不均),這就導(dǎo)致了這個處理器P很忙,但是其他的P還有任務(wù),此時如果global runqueue沒有任務(wù)G了,那么P不得不從其他的P里拿一些G來執(zhí)行。一般來說,如果P從其他的P那里要拿任務(wù)的話,一般就拿run queue的一半,這就確保了每個OS線程都能充分的使用,如下圖:
1、P的數(shù)量:
2、M的數(shù)量:
M與P的數(shù)量沒有絕對關(guān)系,一個M阻塞,P就會去創(chuàng)建或者切換另一個M,所以,即使P的默認(rèn)數(shù)量是1,也有可能會創(chuàng)建很多個M出來。
3、P何時創(chuàng)建:在確定了P的最大數(shù)量n后,運(yùn)行時系統(tǒng)會根據(jù)這個數(shù)量創(chuàng)建n個P。
4、M何時創(chuàng)建:沒有足夠的M來關(guān)聯(lián)P并運(yùn)行其中的可運(yùn)行的G。比如所有的M此時都阻塞住了,而P中還有很多就緒任務(wù),就會去尋找空閑的M,而沒有空閑的,就會去創(chuàng)建新的M。
當(dāng)M因系統(tǒng)調(diào)用而阻塞時(M上運(yùn)行的G進(jìn)入了系統(tǒng)調(diào)用的時候),M與P會分開,如果此時P的就緒隊(duì)列中還有任務(wù),
P就會去關(guān)聯(lián)一個空閑的M,或者創(chuàng)建一個M進(jìn)行關(guān)聯(lián)。(也就是說go不是像libtask一樣處理IO阻塞的?不確定。)
如果一個P的就緒隊(duì)列所有任務(wù)都執(zhí)行完了,那么P會嘗試從其他P的就緒隊(duì)列中取出一部分到自己的就緒隊(duì)列中,以保證每個P的就緒隊(duì)列都有任務(wù)可以執(zhí)行。
關(guān)于Golang協(xié)程調(diào)度的詳細(xì)分析就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。