下面由golang教程欄目給大家介紹Golang 協(xié)程調(diào)度 ,希望對(duì)需要的朋友有所幫助!
創(chuàng)新互聯(lián)建站服務(wù)項(xiàng)目包括蘄春網(wǎng)站建設(shè)、蘄春網(wǎng)站制作、蘄春網(wǎng)頁(yè)制作以及蘄春網(wǎng)絡(luò)營(yíng)銷(xiāo)策劃等。多年來(lái),我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,蘄春網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到蘄春省份的部分城市,未來(lái)相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!一、線程模型N:1模型,N個(gè)用戶空間線程在1個(gè)內(nèi)核空間線程上運(yùn)行。優(yōu)勢(shì)是上下文切換非??斓菬o(wú)法利用多核系統(tǒng)的優(yōu)點(diǎn)。1:1模型,1個(gè)內(nèi)核空間線程運(yùn)行一個(gè)用戶空間線程。這種充分利用了多核系統(tǒng)的優(yōu)勢(shì)但是上下文切換非常慢,因?yàn)槊恳淮握{(diào)度都會(huì)在用戶態(tài)和內(nèi)核態(tài)之間切換。(POSIX線程模型(pthread),Java)M:N模型, 每個(gè)用戶線程對(duì)應(yīng)多個(gè)內(nèi)核空間線程,同時(shí)也可以一個(gè)內(nèi)核空間線程對(duì)應(yīng)多個(gè)用戶空間線程。Go打算采用這種模型,使用任意個(gè)內(nèi)核模型管理任意個(gè)goroutine。這樣結(jié)合了以上兩種模型的優(yōu)點(diǎn),但缺點(diǎn)就是調(diào)度的復(fù)雜性。下面看看golang的協(xié)程調(diào)度
M:一個(gè)用戶空間線程,同時(shí)對(duì)應(yīng)一個(gè)內(nèi)核線程,類(lèi)似posix pthreadP:代表運(yùn)行的上下文環(huán)境, 也就是我們上一節(jié)實(shí)現(xiàn)的調(diào)度器,一個(gè)調(diào)度器也會(huì)對(duì)應(yīng)一個(gè)就緒隊(duì)列G:goroutine,即協(xié)程二、調(diào)度模型簡(jiǎn)介groutine能擁有強(qiáng)大的并發(fā)實(shí)現(xiàn)是通過(guò)GPM調(diào)度模型實(shí)現(xiàn),下面就來(lái)解釋下goroutine的調(diào)度模型。
Go的調(diào)度器內(nèi)部有三個(gè)重要的結(jié)構(gòu):M,P,G
M:M是對(duì)內(nèi)核級(jí)線程的封裝,數(shù)量對(duì)應(yīng)真實(shí)的CPU數(shù),一個(gè)M就是一個(gè)線程,goroutine就是跑在M之上的;M是一個(gè)很大的結(jié)構(gòu),里面維護(hù)小對(duì)象內(nèi)存cache(mcache)、當(dāng)前執(zhí)行的goroutine、隨機(jī)數(shù)發(fā)生器等等非常多的信息
G:代表一個(gè)goroutine,它有自己的棧,instruction pointer和其他信息(正在等待的channel等等),用于調(diào)度。
P:P全稱是Processor,處理器,它的主要用途就是用來(lái)執(zhí)行g(shù)oroutine的。每個(gè)Processor對(duì)象都擁有一個(gè)LRQ(Local Run Queue),未分配的Goroutine對(duì)象保存在GRQ(Global Run Queue )中,等待分配給某一個(gè)P的LRQ中,每個(gè)LRQ里面包含若干個(gè)用戶創(chuàng)建的Goroutine對(duì)象。
Golang采用的是多線程模型,更詳細(xì)的說(shuō)他是一個(gè)兩級(jí)線程模型,但它對(duì)系統(tǒng)線程(內(nèi)核級(jí)線程)進(jìn)行了封裝,暴露了一個(gè)輕量級(jí)的協(xié)程goroutine(用戶級(jí)線程)供用戶使用,而用戶級(jí)線程到內(nèi)核級(jí)線程的調(diào)度由golang的runtime負(fù)責(zé),調(diào)度邏輯對(duì)外透明。goroutine的優(yōu)勢(shì)在于上下文切換在完全用戶態(tài)進(jìn)行,無(wú)需像線程一樣頻繁在用戶態(tài)與內(nèi)核態(tài)之間切換,節(jié)約了資源消耗。
調(diào)度實(shí)現(xiàn)從上圖中看,有2個(gè)物理線程M,每一個(gè)M都擁有一個(gè)處理器P,每一個(gè)也都有一個(gè)正在運(yùn)行的goroutine。
P的數(shù)量可以通過(guò)GOMAXPROCS()來(lái)設(shè)置,它其實(shí)也就代表了真正的并發(fā)度,即有多少個(gè)goroutine可以同時(shí)運(yùn)行。
圖中灰色的那些goroutine并沒(méi)有運(yùn)行,而是出于ready的就緒態(tài),正在等待被調(diào)度。P維護(hù)著這個(gè)隊(duì)列(稱之為runqueue),
Go語(yǔ)言里,啟動(dòng)一個(gè)goroutine很容易:go function 就行,所以每有一個(gè)go語(yǔ)句被執(zhí)行,runqueue隊(duì)列就在其末尾加入一個(gè)
goroutine,在下一個(gè)調(diào)度點(diǎn),就從runqueue中取出(如何決定取哪個(gè)goroutine?)一個(gè)goroutine執(zhí)行。
當(dāng)一個(gè)OS線程M0陷入阻塞時(shí)(如下圖),P轉(zhuǎn)而在運(yùn)行M1,圖中的M1可能是正被創(chuàng)建,或者從線程緩存中取出。
當(dāng)MO返回時(shí),它必須嘗試取得一個(gè)P來(lái)運(yùn)行g(shù)oroutine,一般情況下,它會(huì)從其他的OS線程那里拿一個(gè)P過(guò)來(lái),
如果沒(méi)有拿到的話,它就把goroutine放在一個(gè)global
runqueue里,然后自己睡眠(放入線程緩存里)。所有的P也會(huì)周期性的檢查global
runqueue并運(yùn)行其中的goroutine,否則global runqueue上的goroutine永遠(yuǎn)無(wú)法執(zhí)行。
另一種情況是P所分配的任務(wù)G很快就執(zhí)行完了(分配不均),這就導(dǎo)致了這個(gè)處理器P很忙,但是其他的P還有任務(wù),此時(shí)如果global runqueue沒(méi)有任務(wù)G了,那么P不得不從其他的P里拿一些G來(lái)執(zhí)行。一般來(lái)說(shuō),如果P從其他的P那里要拿任務(wù)的話,一般就拿run queue的一半,這就確保了每個(gè)OS線程都能充分的使用,如下圖:
三、GPM創(chuàng)建相關(guān)問(wèn)題M和P的數(shù)量如何確定?或者說(shuō)何時(shí)會(huì)創(chuàng)建M和P?1、P的數(shù)量:
由啟動(dòng)時(shí)環(huán)境變量$GOMAXPROCS或者是由runtime的方法GOMAXPROCS()決定(默認(rèn)是1)。這意味著在程序執(zhí)行的任意時(shí)刻都只有$GOMAXPROCS個(gè)goroutine在同時(shí)運(yùn)行。2、M的數(shù)量:
go語(yǔ)言本身的限制:go程序啟動(dòng)時(shí),會(huì)設(shè)置M的數(shù)量,默認(rèn)10000.但是內(nèi)核很難支持這么多的線程數(shù),所以這個(gè)限制可以忽略。runtime/debug中的SetMaxThreads函數(shù),設(shè)置M的數(shù)量一個(gè)M阻塞了,會(huì)創(chuàng)建新的M。M與P的數(shù)量沒(méi)有絕對(duì)關(guān)系,一個(gè)M阻塞,P就會(huì)去創(chuàng)建或者切換另一個(gè)M,所以,即使P的默認(rèn)數(shù)量是1,也有可能會(huì)創(chuàng)建很多個(gè)M出來(lái)。
3、P何時(shí)創(chuàng)建:在確定了P的數(shù)量n后,運(yùn)行時(shí)系統(tǒng)會(huì)根據(jù)這個(gè)數(shù)量創(chuàng)建n個(gè)P。
4、M何時(shí)創(chuàng)建:沒(méi)有足夠的M來(lái)關(guān)聯(lián)P并運(yùn)行其中的可運(yùn)行的G。比如所有的M此時(shí)都阻塞住了,而P中還有很多就緒任務(wù),就會(huì)去尋找空閑的M,而沒(méi)有空閑的,就會(huì)去創(chuàng)建新的M。
M選擇哪一個(gè)P關(guān)聯(lián)?M會(huì)選擇導(dǎo)致此M被創(chuàng)建的那個(gè)P關(guān)聯(lián)。什么時(shí)候會(huì)切換P與M的關(guān)聯(lián)關(guān)系?當(dāng)M因系統(tǒng)調(diào)用而阻塞時(shí)(M上運(yùn)行的G進(jìn)入了系統(tǒng)調(diào)用的時(shí)候),M與P會(huì)分開(kāi),如果此時(shí)P的就緒隊(duì)列中還有任務(wù),
P就會(huì)去關(guān)聯(lián)一個(gè)空閑的M,或者創(chuàng)建一個(gè)M進(jìn)行關(guān)聯(lián)。(也就是說(shuō)go不是像libtask一樣處理IO阻塞的?不確定。)
如果一個(gè)P的就緒隊(duì)列所有任務(wù)都執(zhí)行完了,那么P會(huì)嘗試從其他P的就緒隊(duì)列中取出一部分到自己的就緒隊(duì)列中,以保證每個(gè)P的就緒隊(duì)列都有任務(wù)可以執(zhí)行。
網(wǎng)站欄目:關(guān)于Golang協(xié)程調(diào)度
本文路徑:http://weahome.cn/article/cjcghd.html