閱讀Go并發(fā)編程對(duì)go語(yǔ)言線(xiàn)程模型的筆記,解釋的非常到,好記性不如爛筆頭,忘記的時(shí)候回來(lái)翻一番,在此做下筆記。
Go語(yǔ)言的線(xiàn)程實(shí)現(xiàn)模型,又3個(gè)必知的核心元素,他們支撐起了這個(gè)線(xiàn)程實(shí)現(xiàn)模型的主要框架:
1>M:Machine的縮寫(xiě)。一個(gè)M代表一個(gè)內(nèi)核線(xiàn)程。
2>P:Procecssor的縮寫(xiě)。一個(gè)P代表了M所在的上下文環(huán)境。
3>G:Goroutine的縮寫(xiě)。一個(gè)G代表了對(duì)一段需要被并發(fā)執(zhí)行的Go語(yǔ)言代碼的封裝。
簡(jiǎn)單的來(lái)說(shuō),一個(gè)G的執(zhí)行文件需要M和P的支持。一個(gè)M在與一個(gè)P關(guān)聯(lián)形成一個(gè)有效的G運(yùn)行環(huán)境(內(nèi)核線(xiàn)程+上下文環(huán)境)。
每個(gè)P都會(huì)包含一個(gè)可運(yùn)行的G的隊(duì)列(runq)。該隊(duì)列的G會(huì)被依次傳給與本地P關(guān)聯(lián)的M并獲得運(yùn)行時(shí)機(jī)。在這里,
我們把運(yùn)行當(dāng)前程序的那個(gè)M稱(chēng)為當(dāng)前M,而把與當(dāng)前M關(guān)聯(lián)的那個(gè)P稱(chēng)為本地P。
M(Machine)與KSE(Kernel Schedule Entity)之間總一對(duì)一的。一個(gè)M能且僅代表一個(gè)內(nèi)核線(xiàn)程。Go語(yǔ)言的運(yùn)行
時(shí)系統(tǒng)(runtime system)用它來(lái)代表一個(gè)內(nèi)核調(diào)度系統(tǒng)。
1.M(Machine)
一個(gè)M代表了一個(gè)內(nèi)核線(xiàn)程。大多數(shù)情況下,創(chuàng)建一個(gè)M的原因都是由于沒(méi)有足夠的M來(lái)關(guān)聯(lián)P(Process)
并運(yùn)行其中的可運(yùn)行的G。不過(guò),在運(yùn)行時(shí)系統(tǒng)執(zhí)行系統(tǒng)監(jiān)控或垃圾回收等任務(wù)的時(shí)候也會(huì)
導(dǎo)致新的M的創(chuàng)建。M(Machine)的數(shù)據(jù)結(jié)構(gòu)包括(curg p mstartfn nextp)。

M(Machine)結(jié)構(gòu)中的字段眾多。我們?cè)谶@里只是挑選了對(duì)于我們的初步認(rèn)識(shí)M(Machine)最重要的4個(gè)字段。其中字段
curg會(huì)存放當(dāng)前M正在運(yùn)行的那個(gè)G(goroutine)的指針,字段p會(huì)指向與當(dāng)前M相關(guān)聯(lián)的那個(gè)P,而字段mstartfm則代表
我們馬上會(huì)講到的M(Machine)的起始函數(shù)。在M被調(diào)度的過(guò)程中,這三個(gè)字段最能體現(xiàn)他的即使情況。而另外的字段nextp則
會(huì)被用于暫存與當(dāng)前M(Machine)又潛在關(guān)系的P。我們可以把調(diào)度器將某個(gè)P(Process)賦值給某個(gè)M的nextp字段的操作稱(chēng)為
M和P的預(yù)聯(lián)。在有些時(shí)候,運(yùn)行時(shí)系統(tǒng)給會(huì)把剛剛被重啟新啟用的M(Machine)和它預(yù)聯(lián)的那個(gè)P關(guān)聯(lián)在一起,這就是nextp字段的所起到的作用。
M被創(chuàng)建之初會(huì)被加入全局的M(Machine)列表(runtie.allm)中。緊接著,它的起始函數(shù)和準(zhǔn)備關(guān)聯(lián)的P(Process)(大多數(shù)
情況下導(dǎo)致次M(Machine)創(chuàng)建操作的那個(gè)P(Process))會(huì)被設(shè)置。最后,運(yùn)行時(shí)系統(tǒng)會(huì)為它專(zhuān)門(mén)創(chuàng)建一個(gè)新的內(nèi)核線(xiàn)程并與之
關(guān)聯(lián)。這樣,這個(gè)新的M(Machine)就為執(zhí)行G(Goroutine)做好了準(zhǔn)備。而這里的全局M(Machine)列表其實(shí)并沒(méi)有什么特殊的意義。
運(yùn)行時(shí)系統(tǒng)在需要的時(shí)候會(huì)通過(guò)它獲取所有M的信息。同時(shí)它也防止M被當(dāng)作垃圾回收。
在新的M被創(chuàng)建完成之后的會(huì)先進(jìn)行一番初始化工作。其中包括了對(duì)自身所持的??臻g以及信號(hào)處理方面的初始化。
在這些初始化工作都完成之后。該M將會(huì)被執(zhí)行(如果存在的話(huà))。注意,如果在這個(gè)起始函數(shù)代表的是系統(tǒng)監(jiān)控的任務(wù)
的話(huà),那么該M會(huì)一直在那里執(zhí)行而不會(huì)繼續(xù)后面的流程。否則,在初始函數(shù)被執(zhí)行完畢后。當(dāng)前M將會(huì)與那個(gè)準(zhǔn)備與
它關(guān)聯(lián)的P完成關(guān)聯(lián)。至此,一個(gè)并發(fā)執(zhí)行環(huán)境才真正的形成。在這之后,M開(kāi)始尋找可運(yùn)行的G并運(yùn)行它,這一過(guò)程
可以被看做是調(diào)度的一部分。
運(yùn)行時(shí)系統(tǒng)所管轄的M(或者說(shuō)runtime.allm中的M)有時(shí)候會(huì)被停止,比如在運(yùn)行時(shí)系統(tǒng)準(zhǔn)備開(kāi)始執(zhí)行垃圾回收任務(wù)時(shí)候。
運(yùn)行時(shí)系統(tǒng)停止在M的時(shí)候,會(huì)對(duì)它的屬性進(jìn)行必要的重置之后,把它放進(jìn)調(diào)度器的空閑M列表(runtime.sched.midle)。
因?yàn)樵谛枰粋€(gè)未被使用的M的時(shí)候,運(yùn)行時(shí)系統(tǒng)會(huì)嘗試從該列表中。
注意,M本身是無(wú)狀態(tài)的。M是否空閑僅僅以為它是否存在于調(diào)度器的空閑M列表中為依據(jù)。雖然運(yùn)行時(shí)系統(tǒng)可以通過(guò)M列表
獲取所有的M,但是卻無(wú)法得知它們的狀態(tài)(因?yàn)樗鼈儧](méi)有狀態(tài))。
單個(gè)Go程序所使用的M最大數(shù)據(jù)是可以被設(shè)置的。在我們使用命令運(yùn)行Go程序的時(shí)候,一個(gè)引導(dǎo)程序先會(huì)被啟動(dòng)。
這個(gè)引導(dǎo)程序先會(huì)被啟動(dòng),這個(gè)初始值是1w。也就是說(shuō),一個(gè)Go程序最多可以使用1w個(gè)M。
這就以為著。在最理想的情況下,同時(shí)可以有1w個(gè)內(nèi)核線(xiàn)程同時(shí)被執(zhí)行。請(qǐng)注意,這里說(shuō)的是最理想的i情況下的。
由于操作系統(tǒng)的內(nèi)核對(duì)進(jìn)程的虛擬內(nèi)存的布局的控制以及大小的限制,如此量級(jí)的線(xiàn)程很難共存。從這個(gè)角度看。
Go語(yǔ)言本身對(duì)線(xiàn)程的線(xiàn)程數(shù)量幾乎可以被忽略。
出了上述設(shè)置外,我們也可以在Go程序中對(duì)該限制進(jìn)行設(shè)置。為了達(dá)到此目的,我們需要調(diào)用標(biāo)準(zhǔn)庫(kù)的代碼包runtime/debug包
中的SetMaxThreads函數(shù)并且對(duì)提供新的M最大數(shù)量。runtime/debug.SetMaxThreads函數(shù)在執(zhí)行后,會(huì)把舊的M最大數(shù)量作為結(jié)果
值返回。非常重要的一點(diǎn)是,如果我嫩在調(diào)用runtime/debug.seMaxThreads函數(shù)時(shí)給定的新值比當(dāng)時(shí)M的實(shí)際數(shù)量還要小的話(huà),
運(yùn)行時(shí)系統(tǒng)就會(huì)發(fā)起一個(gè)運(yùn)行時(shí)恐慌。所以,我們要小心使用這個(gè)函數(shù)。請(qǐng)記住,如果我們真的需要設(shè)置M的最大數(shù)量。
那么也早調(diào)用runtime/debug.SetMaxThreads函數(shù)就也好,對(duì)于它的設(shè)定值,我們也要仔細(xì)斟酌。
2.P(Process)
P(Process)是使G能夠在M中運(yùn)行的關(guān)鍵。Golang的運(yùn)行時(shí)系統(tǒng)會(huì)實(shí)時(shí)地讓P與不同的M建立或斷開(kāi)關(guān)聯(lián),以使P中的那些可運(yùn)行的
G能夠在需要的時(shí)候及時(shí)獲得運(yùn)行時(shí)機(jī)。這與操作系統(tǒng)內(nèi)核在CPU之上切換不同的進(jìn)程或者線(xiàn)程類(lèi)似。
通過(guò)調(diào)用函數(shù)runtime.GOMAXPROCS,我們可以改變單個(gè)Go程序可以間接擁有的P的最大數(shù)量。初除此自外,我們還可以在運(yùn)行Go程序
之前設(shè)置環(huán)境變量GOMAXPROCS的值對(duì)Go程序的可以用的P最大的數(shù)量做出預(yù)先設(shè)定。P的最大數(shù)量相當(dāng)于是對(duì)可以被并發(fā)運(yùn)行的用戶(hù)
級(jí)別的G的數(shù)量做出限制。我們已經(jīng)知道,每個(gè)P都需要關(guān)聯(lián)一個(gè)M(Machine)才能使其中的可運(yùn)行的G得到執(zhí)行。但是這卻不意味著
環(huán)境變量GOMAXPROCS的值會(huì)被限制住M的總數(shù)量。當(dāng)M因系統(tǒng)調(diào)用的進(jìn)行而被阻塞(更切確的說(shuō),是它運(yùn)行的G進(jìn)入了系統(tǒng)的調(diào)用)的
時(shí)候,運(yùn)行時(shí)系統(tǒng)會(huì)將該M和與之關(guān)聯(lián)的P分離出來(lái)。這時(shí),如果這個(gè)P的可運(yùn)行G隊(duì)列中還未被運(yùn)行的G,那么運(yùn)行時(shí)系統(tǒng)
就會(huì)找到一個(gè)空閑M,或創(chuàng)建出一個(gè)新的M,并與該P(yáng)關(guān)聯(lián)以滿(mǎn)足這些G運(yùn)行需要。如果我們?cè)贕o程序中創(chuàng)建大部分Goroutine中
都包含了很多需要的間接地進(jìn)行各種系統(tǒng)調(diào)用(比如各種I/O操作)代碼的話(huà),那么即使環(huán)境變量GOMAXPROCS的值被設(shè)定未1,也
很可能被創(chuàng)建很多個(gè)M被創(chuàng)建出來(lái)。所以,實(shí)際的M總數(shù)量很可能比環(huán)境變量GOMAXPROCS所指代的數(shù)量多。由此可見(jiàn),Go程序
真正使用的內(nèi)核線(xiàn)程的數(shù)量并不會(huì)因此而受到限制。
在Go程序開(kāi)始被運(yùn)行的時(shí)候,我們?cè)谇懊嫣岬降囊龑?dǎo)程序也會(huì)對(duì)P的最大數(shù)量進(jìn)行設(shè)置。P的最大數(shù)量的默認(rèn)值是1。因此。
在默認(rèn)的情況下,無(wú)論我們?cè)诔绦蛑杏胓o語(yǔ)句啟用多個(gè)Goroutine。它們都只會(huì)被塞入同一個(gè)P的可運(yùn)行G的隊(duì)列中,當(dāng)
環(huán)境變量GOMAXPROCS的值的有效就會(huì)被這個(gè)硬性限制取代,也就是說(shuō),最終的P最大數(shù)量值絕對(duì)不會(huì)比引導(dǎo)程序中的這個(gè)硬性
上線(xiàn)值打。該硬性上限值是2的8次方。即256.這個(gè)硬性上限值為256的原因是Go語(yǔ)言目前還不能保證在數(shù)量比256更多的P同時(shí)存在的
情況下Go程序仍能保持高效。也就是說(shuō),這個(gè)硬行上線(xiàn)并不是永久的,它在以后可能會(huì)被改變
[https://stackoverflow.com/questions/40943065/golang-why-runtime-gomaxprocs-is-limited-to-256]現(xiàn)在是1024了。
注意,雖然我們可以在程序中隨意地調(diào)用runtime.GOMAXPROCS函數(shù),但是它的執(zhí)行會(huì)暫時(shí)使所有的P都相繼進(jìn)入停止?fàn)顟B(tài)并試圖
阻止任何用戶(hù)級(jí)別的G的運(yùn)行。只有在新的P最大數(shù)量被設(shè)定完成后,運(yùn)行時(shí)系統(tǒng)才會(huì)開(kāi)始陸續(xù)恢復(fù)它們。對(duì)于程序的性能是
非常大的損耗。所以,我們只好在Go程序的main函數(shù)的開(kāi)始處調(diào)用runtime.GOMAXPROCS函數(shù)。當(dāng)然,在Go程序中不對(duì)它進(jìn)行
調(diào)用而只預(yù)先設(shè)置環(huán)境變量GOMAXPROCS是最好不過(guò)的了
在確定P的最大數(shù)量之后,運(yùn)行時(shí)系統(tǒng)會(huì)根據(jù)這個(gè)數(shù)值初始化全局的P列表(runtime.allp)與全局M列表類(lèi)似,該列表包含了當(dāng)前
運(yùn)行時(shí)的系統(tǒng)創(chuàng)建的所有P。隨后,運(yùn)行時(shí)系統(tǒng)會(huì)把調(diào)度器的可運(yùn)行G隊(duì)列(runtime.sched.runq)中的所有G均勻的放入到全局
列表中。至此,運(yùn)行時(shí)系統(tǒng)需要用到的所有P都已就緒
與空閑M列表類(lèi)似,所運(yùn)行時(shí)系統(tǒng)中也存在一個(gè)調(diào)度器的空閑P列表(runtime.sched.pidle)。當(dāng)一個(gè)P不再與任何M關(guān)聯(lián)的時(shí)候,
運(yùn)行時(shí)系統(tǒng)就會(huì)把它放入到該列表,當(dāng)前運(yùn)行時(shí)系統(tǒng)需要一個(gè)空閑的P關(guān)聯(lián)某個(gè)M的話(huà),會(huì)從次列表取一個(gè)出來(lái),由此我們也可知道
空閑P列表的準(zhǔn)入條件,注意,即使P進(jìn)入到了空閑P列表中,它的運(yùn)行G列表也不一定是空的,兩者之間沒(méi)有必然的聯(lián)系。
與M不同,P本身是有狀態(tài)的,一個(gè)P可能具有的狀態(tài)如下:
1>Pidle: 此狀態(tài)表明當(dāng)前P未與任何M存在關(guān)聯(lián)。
2>Prunning:此狀態(tài)表明當(dāng)前P與某個(gè)M關(guān)聯(lián)。
3>Psyscall:此狀態(tài)表明當(dāng)前P中的被運(yùn)行的那個(gè)G正在進(jìn)行系統(tǒng)調(diào)用。
4>Pgcstop:此狀態(tài)表明運(yùn)行系統(tǒng)正在驚醒垃圾回收,在運(yùn)行時(shí)系統(tǒng)驚醒垃圾回收的時(shí)候,會(huì)試圖把全局列表中的都置于此狀態(tài)。
5>Pdead:此狀態(tài)表明當(dāng)前P已經(jīng)不會(huì)再被調(diào)用。當(dāng)我們Go程序運(yùn)行的過(guò)程中通過(guò)調(diào)用。

runtime.GOMAXPROCS函數(shù)減少P最大數(shù)量的時(shí)候,其余的P就會(huì)被運(yùn)行時(shí)系統(tǒng)置于此狀態(tài)。P的初始狀態(tài)是Pgcstop,
雖然運(yùn)行時(shí)系統(tǒng)并不會(huì)再這時(shí)進(jìn)行垃圾回收。不過(guò),P處于這一初始狀態(tài)的時(shí)間會(huì)非常短暫。緊接著的初始化和填充P中的可
運(yùn)行G隊(duì)列之后,運(yùn)行時(shí)系統(tǒng)會(huì)被其狀態(tài)設(shè)置未Pidle并放入到調(diào)度器的空閑列表中。此空閑P列表中的所有P都有調(diào)度器根據(jù)實(shí)際
情況經(jīng)進(jìn)行取用。
3.G(Goroutine)
一個(gè)G就相當(dāng)于一個(gè)Goroutine(或稱(chēng)Go程),也與我們使用go語(yǔ)句欲并發(fā)執(zhí)行的一個(gè)匿名或命名的函數(shù)相對(duì)應(yīng)。我們
作為編程人員只使用go語(yǔ)句向Go語(yǔ)言的運(yùn)行時(shí)系統(tǒng)告知了(或提交了)一個(gè)個(gè)并發(fā)任務(wù),而Go語(yǔ)言的運(yùn)行時(shí)系統(tǒng)則會(huì)
按照我們的要求并發(fā)地執(zhí)行完成這一任務(wù)。
Go語(yǔ)言的編譯器會(huì)把我們編寫(xiě)的go語(yǔ)句(go 關(guān)鍵字和其后的函數(shù)統(tǒng)稱(chēng))變成對(duì)一個(gè)運(yùn)行時(shí)系統(tǒng)中的函數(shù)調(diào)用,并把go
語(yǔ)句中的那個(gè)函數(shù)以及其參數(shù)都作為參數(shù)傳遞給這個(gè)運(yùn)行時(shí)系統(tǒng)中的函數(shù)。這也是我們應(yīng)該了解的第一件與go語(yǔ)句相關(guān)
的事。其實(shí)它并不神奇,只是代表了我們向運(yùn)行時(shí)系統(tǒng)遞交了一個(gè)任務(wù)而已。
運(yùn)行時(shí)系統(tǒng)在接到這樣一個(gè)調(diào)用之后,會(huì)先檢查一下go函數(shù)及其參數(shù)的合法性,緊接著會(huì)試圖從本地P的自由G列表和調(diào)度器
的自由G列表獲取可用的G。如果沒(méi)有獲取到則只好新建一個(gè)G了。與M和P相同,運(yùn)行時(shí)系統(tǒng)也持有一個(gè)G的全局列表(runtime.allg)。
新建立的G會(huì)在第一時(shí)間被加入該列表中。類(lèi)似地,該列表的主要作用也就是集中存放當(dāng)前運(yùn)行時(shí)系統(tǒng)中的所有G指針。無(wú)論
將會(huì)封裝當(dāng)前的這個(gè)go函數(shù)的G是否是最新的,運(yùn)行時(shí)系統(tǒng)都會(huì)對(duì)它進(jìn)行一次初始化。其中包裹了關(guān)聯(lián)的go函數(shù)以及設(shè)置G的
狀態(tài)和ID等步驟。在初始化完成后,這個(gè)G會(huì)被放入到本地P的可運(yùn)行G隊(duì)列中。如果實(shí)際成熟,調(diào)度會(huì)立即進(jìn)行以使這個(gè)G盡快
運(yùn)行。不過(guò)為了及時(shí)運(yùn)行各個(gè)可運(yùn)行的忙碌著。
每個(gè)G都會(huì)由運(yùn)行時(shí)系統(tǒng)根絕其實(shí)際狀態(tài)情況設(shè)置不同的狀態(tài),其可能的狀態(tài)如下。
1>Gidle: 在當(dāng)前G被創(chuàng)建但還沒(méi)有完全未被初始化的時(shí)候會(huì)處于此狀態(tài)。
2>Grunnable:表示當(dāng)前G是可運(yùn)行時(shí)的,并且正在等待被與運(yùn)行。
3>Grunning:表示當(dāng)前G正在被運(yùn)行。
4>Gsyscall:表示當(dāng)前G正在進(jìn)行系統(tǒng)調(diào)用。
5>Gwaiting:表示當(dāng)前G正在因某個(gè)原因而等待。
6>Gdead:表示當(dāng)前G已經(jīng)被運(yùn)行完成
在運(yùn)行時(shí)系統(tǒng)想用一個(gè)G封我們通過(guò)go語(yǔ)句遞交的go函數(shù)的時(shí)候,會(huì)對(duì)這個(gè)G進(jìn)行初始化。其中的一步就是初始化這個(gè)G的
狀態(tài),而這個(gè)狀態(tài)總會(huì)是Grunnable。也就是說(shuō),一個(gè)G真正的開(kāi)始被使用是在其狀態(tài)被設(shè)置Grunnable之后。

一個(gè)G在被運(yùn)行的過(guò)程中,時(shí)候會(huì)等待某個(gè)事件以及會(huì)等待什么樣的事件,完全由其封裝的go函數(shù)決定的。例如,
如果這個(gè)函數(shù)中包含了對(duì)通道類(lèi)型值的操作,那么在執(zhí)行到對(duì)應(yīng)的代碼的時(shí)候這個(gè)G就有可能進(jìn)入Gwaiting狀態(tài)。
這可能在等待從通道類(lèi)型值中接受值,也可能是在等待向通道類(lèi)型發(fā)送值。又例如,設(shè)計(jì)網(wǎng)絡(luò)I/O的時(shí)候也會(huì)導(dǎo)致
相應(yīng)的G進(jìn)入Gwaiting狀態(tài)。此外,操作定時(shí)器(time.Timer)和調(diào)用time.Sleep函數(shù)同樣會(huì)造成相應(yīng)的G的等待。在事件到來(lái)
之后,G會(huì)被"喚醒"并被轉(zhuǎn)移到Grunnable狀態(tài)。待時(shí)機(jī)來(lái)時(shí),它會(huì)在此執(zhí)行。
G在退出系統(tǒng)調(diào)用的時(shí)候的狀態(tài)轉(zhuǎn)換要比上述情況發(fā)雜一些,運(yùn)行時(shí)系統(tǒng)會(huì)先嘗試直接運(yùn)行這個(gè)G,僅當(dāng)無(wú)法直接運(yùn)行的時(shí)候,才
會(huì)把它轉(zhuǎn)換成Grunnable狀態(tài)并放入到調(diào)度器放入自由G列表中,顯然,對(duì)這樣的一個(gè)G來(lái)說(shuō),在其退出系統(tǒng)之時(shí)就被立即繼續(xù)運(yùn)行
是再好不過(guò)的了。運(yùn)行時(shí)系統(tǒng)當(dāng)然會(huì)為此做出一些努力,不過(guò),即使努力失敗了,該G也還是在實(shí)時(shí)的調(diào)度過(guò)程中被發(fā)現(xiàn)并運(yùn)行。
最后,值得一提的是,進(jìn)入死亡狀態(tài)(Gdead)的G是可以被重新初始化并使用的。相比之下,P在進(jìn)入狀態(tài)(Pdead)之后則
只能面臨銷(xiāo)毀的結(jié)局。由此可以說(shuō)明Gdead狀態(tài)與Pdead狀態(tài)所表達(dá)的含義是截然不同的。初一Gdead狀態(tài)的G會(huì)被放入本地P或
調(diào)度器的自由G列表,這為它們的重要條件。
至此,我們了解到一個(gè)G在運(yùn)行時(shí)系統(tǒng)中的流轉(zhuǎn)方式和時(shí)機(jī),著也展現(xiàn)了一條go語(yǔ)句的背后所蘊(yùn)含的玄機(jī)。
核心元素容器

在這些容器中,全局的那個(gè)3個(gè)列表存在的主要目的都分別是為了統(tǒng)計(jì)運(yùn)行時(shí)系統(tǒng)中的所有M,P或G。
相比之下。最應(yīng)該值得我們關(guān)注的是那些非全局的容器,尤其是與G相關(guān)的那4個(gè)容器。
與G有關(guān)的非全局容器有可運(yùn)行G隊(duì)列,調(diào)度器的自由G列表,本地P的可運(yùn)行G隊(duì)列以及本地P的自由G列表。
運(yùn)行時(shí)系統(tǒng)創(chuàng)建出的任何G都回存在于全局G列表中,而其余的4個(gè)列表則只存放在當(dāng)前作用域的所有特定
的狀態(tài)的G。注意,這里的兩個(gè)可運(yùn)行G列表中的G都擁有幾乎平等的運(yùn)行機(jī)會(huì)。由于這種平等性的存在,所以
我們無(wú)需關(guān)心哪類(lèi)可運(yùn)行的G會(huì)進(jìn)入到哪一個(gè)隊(duì)列中,不過(guò)??梢皂槺闾嵋幌拢瑥腉syscall狀態(tài)和Ggstop狀態(tài)轉(zhuǎn)出
的G,都會(huì)被放入調(diào)度器的可運(yùn)行G隊(duì)列,而被運(yùn)行時(shí)系統(tǒng)初始化的G,都會(huì)被放入本地P的可運(yùn)行G隊(duì)列。至于
從Gwaiting狀態(tài)轉(zhuǎn)出的G,除了因進(jìn)行網(wǎng)絡(luò)I/O而陷入等待的G之外,都會(huì)被存放到本地P的可運(yùn)行G隊(duì)列。此外,
我們之前說(shuō)過(guò),對(duì)runtine.GOMAXPROCS函數(shù)的調(diào)用,可能會(huì)導(dǎo)致運(yùn)行時(shí)系統(tǒng)清空調(diào)度器的可運(yùn)行G隊(duì)列。其中的
所有G都會(huì)被均勻地放入到全局P列表這種的各個(gè)P的可運(yùn)行G對(duì)了當(dāng)中。另一方面在G轉(zhuǎn)入Gdead狀態(tài)后,首先會(huì)被
放入本地P的自由G列表,而在運(yùn)行時(shí)系統(tǒng)需要用自由G封裝go函數(shù)的時(shí)候,也會(huì)嘗試從本地P的自由G列表中獲取。
調(diào)度器的自由G列表只是起到了一個(gè)暫存自由G的作用。
與M和P相關(guān)的非全局容器分別是調(diào)度器的空閑M列表和調(diào)度器的空閑P列表。這兩個(gè)列表都被用于存放暫時(shí)不被
使用的元素的實(shí)例。在運(yùn)行時(shí)系統(tǒng)有需要的時(shí)候,會(huì)從中獲取i相應(yīng)的元素的實(shí)例重新啟動(dòng)該它。