go為什么這么快漱抓?(再探GMP模型)

進(jìn)程、線程恕齐、協(xié)程

進(jìn)程:進(jìn)程是系統(tǒng)進(jìn)行資源分配的基本單位乞娄,有獨(dú)立的內(nèi)存空間,單切換代價(jià)極高,進(jìn)程間通信也比較麻煩

線程:線程是CPU調(diào)度和分派的基本單位仪或,線程依附于進(jìn)程确镊,與其他線程共享進(jìn)程的資源,僅有自己的(程序計(jì)數(shù)器范删,一組寄存器的值蕾域,和棧),線程切換代價(jià)械降(但是線程之間的切換可能會(huì)設(shè)計(jì)用戶態(tài)和內(nèi)核態(tài)的切換)束铭,由于共享進(jìn)程資源,所以線程之間通信比較方便厢绝。

協(xié)程協(xié)程是一種用戶態(tài)的輕量級(jí)線程契沫,協(xié)程的調(diào)度完全由用戶控制,協(xié)程切換只需要保存和恢復(fù)任務(wù)的上下文昔汉,沒(méi)有內(nèi)核的開(kāi)銷懈万。協(xié)程間通信也比較簡(jiǎn)單(協(xié)程間本身是不可搶占的,由于操作系統(tǒng)的調(diào)度機(jī)制無(wú)法影響到它靶病,因此一般存在用戶自定義的調(diào)度機(jī)制)(也可以這么說(shuō)內(nèi)核線程依然叫“線程(thread)”会通,用戶線程叫“協(xié)程(co-routine)".)

Golang為并發(fā)而生

Goroutine非常輕量,主要體現(xiàn)在以下方面:

  1. 上下文切換代價(jià)小娄周,Goroutine的上下文切換只涉及到三個(gè)寄存器(PC/SP/DX)的值的修改涕侈,而線程的切換需要涉及模式轉(zhuǎn)換,以及16個(gè)寄存器的刷新煤辨。
  2. 內(nèi)存占用少裳涛,線程棧空間一般是2M,而goroutine只需要2k;

Go的調(diào)度器實(shí)現(xiàn)機(jī)制

Go程序通過(guò)調(diào)度器來(lái)調(diào)度Goroutine在內(nèi)核級(jí)線程上執(zhí)行众辨,但是并不直接綁定os線程M-Machine運(yùn)行端三,而是由Goroutine Scheduler中的 P-processor作獲取內(nèi)核線程資源的【中介】

Go的調(diào)度器通常被稱為G-M-P模型,實(shí)際包含四個(gè)結(jié)構(gòu)鹃彻,分別為:
G:Goroutine郊闯,每個(gè)Gotoutine對(duì)應(yīng)一個(gè)G結(jié)構(gòu)體,G存儲(chǔ)Goroutine的運(yùn)行堆棧蛛株,狀態(tài)团赁,以及任務(wù)函數(shù),可重用(函數(shù)實(shí)體)G需要保存到P才能被調(diào)度執(zhí)行

M:machine谨履,os內(nèi)核線程抽象欢摄,代表真正執(zhí)行計(jì)算的資源,在綁定有效的P后,進(jìn)入schedule循環(huán)屉符;而shcedule循環(huán)的機(jī)制大致是從Global隊(duì)列剧浸,P的local隊(duì)列以及wait隊(duì)列中獲取锹引。

M的數(shù)量是不固定的矗钟,有Go Runtime調(diào)整唆香,為了防止創(chuàng)建過(guò)多OS線程導(dǎo)致系統(tǒng)調(diào)度不過(guò)來(lái),目前默認(rèn)設(shè)置為10000個(gè)吨艇,M不保存G的上下文躬它,這是G可以跨M的基礎(chǔ)。

P:Processor,表示邏輯處理器东涡,對(duì)G來(lái)說(shuō)冯吓,P相當(dāng)于CPU核,G只有綁定到P才能被調(diào)度疮跑。對(duì)M來(lái)說(shuō)组贺,P提供了相關(guān)的執(zhí)行環(huán)境,入內(nèi)存分配狀態(tài)祖娘,任務(wù)隊(duì)列等失尖。
P 的數(shù)量決定了系統(tǒng)內(nèi)最大可并行的 G 的數(shù)量(前提:物理 CPU 核數(shù) >= P 的數(shù)量)。
P 的數(shù)量由用戶設(shè)置的 GoMAXPROCS 決定渐苏,但是不論 GoMAXPROCS 設(shè)置為多大掀潮,P 的數(shù)量最大為 256。

Sche:Go調(diào)度器琼富,它維護(hù)有存儲(chǔ)M和G的隊(duì)列以及調(diào)度器的一些狀態(tài)信息等仪吧。

調(diào)度器循環(huán)的機(jī)制大致是從各種隊(duì)列、P 的本地隊(duì)列中獲取 G鞠眉,切換到 G 的執(zhí)行棧上并執(zhí)行 G 的函數(shù)薯鼠,調(diào)用 Goexit 做清理工作并回到 M,如此反復(fù)械蹋。

可以通過(guò)經(jīng)典的地鼠推車搬磚的模型來(lái)說(shuō)明其三者關(guān)系:

image

地鼠(Gopher)的工作任務(wù)是:工地上有若干磚頭人断,地鼠借助小車把磚頭運(yùn)送到火種上去燒制。M 就可以看作圖中的地鼠朝蜘,P 就是小車恶迈,G 就是小車?yán)镅b的磚。

Processor(P)
根據(jù)用戶設(shè)置的 GoMAXPROCS 值來(lái)創(chuàng)建一批小車(P)谱醇。

Goroutine(G)
通過(guò) Go 關(guān)鍵字就是用來(lái)創(chuàng)建一個(gè) Goroutine暇仲,也就相當(dāng)于制造一塊磚(G),然后將這塊磚(G)放入當(dāng)前這輛小車(P)中副渴。

Machine (M)
地鼠(M)不能通過(guò)外部創(chuàng)建出來(lái)奈附,只能磚(G)太多了,地鼠(M)又太少了煮剧,實(shí)在忙不過(guò)來(lái)斥滤,剛好還有空閑的小車(P)沒(méi)有使用将鸵,那就從別處再借些地鼠(M)過(guò)來(lái)直到把小車(P)用完為止。

這里有一個(gè)地鼠(M)不夠用佑颇,從別處借地鼠(M)的過(guò)程顶掉,這個(gè)過(guò)程就是創(chuàng)建一個(gè)內(nèi)核線程(M)

需要注意的是:地鼠(M) 如果沒(méi)有小車(P)是沒(méi)辦法運(yùn)磚的挑胸,小車(P)的數(shù)量決定了能夠干活的地鼠(M)數(shù)量痒筒,在 Go 程序里面對(duì)應(yīng)的是活動(dòng)線程數(shù);

在Go程序里茬贵,我們也可以通過(guò)下面的圖示來(lái)展示G-M-P模型簿透。

image

Go 調(diào)度器中有兩個(gè)不同的運(yùn)行隊(duì)列:全局運(yùn)行隊(duì)列(GRQ)和本地運(yùn)行隊(duì)列(LRQ)。

每個(gè) P 都有一個(gè) LRQ解藻,用于管理分配給在 P 的上下文中執(zhí)行的 Goroutines老充,這些 Goroutine 輪流被和 P 綁定的 M 進(jìn)行上下文切換。GRQ 適用于尚未分配給 P 的 Goroutines螟左。

從上圖可以看出啡浊,G 的數(shù)量可以遠(yuǎn)遠(yuǎn)大于 M 的數(shù)量,換句話說(shuō)路狮,Go 程序可以利用少量的內(nèi)核級(jí)線程來(lái)支撐大量 Goroutine 的并發(fā)M:N模型虫啥。多個(gè) Goroutine 通過(guò)用戶級(jí)別的上下文切換來(lái)共享內(nèi)核線程 M 的計(jì)算資源,但對(duì)于操作系統(tǒng)來(lái)說(shuō)并沒(méi)有線程上下文切換產(chǎn)生的性能損耗奄妨。

為了更加充分利用線程的計(jì)算資源涂籽,Go 調(diào)度器采取了以下幾種調(diào)度策略

任務(wù)竊取
為了提高 Go 并行處理能力,調(diào)高整體處理效率砸抛,當(dāng)每個(gè) P 之間的 G 任務(wù)不均衡時(shí)评雌,調(diào)度器允許從 GRQ,或者其他 P 的 LRQ 中獲取 G 執(zhí)行直焙。

減少阻塞
在Go里阻塞主要分為以下4個(gè)場(chǎng)景:

1.由于原子景东、互斥量或channel操作調(diào)用導(dǎo)致阻塞,調(diào)度器將把當(dāng)前阻塞的 Goroutine 切換出去奔誓,重新調(diào)度 LRQ 上的其他 Goroutine斤吐。

2.由于網(wǎng)絡(luò)請(qǐng)求和 IO 操作導(dǎo)致 Goroutine 阻塞
Go 程序提供了網(wǎng)絡(luò)輪詢器(NetPoller)來(lái)處理網(wǎng)絡(luò)請(qǐng)求和 IO 操作的問(wèn)題,其后臺(tái)通過(guò) kqueue(MacOS)厨喂,epoll(Linux)或 iocp(Windows)來(lái)實(shí)現(xiàn) IO 多路復(fù)用和措。

通過(guò)使用 NetPoller 進(jìn)行網(wǎng)絡(luò)系統(tǒng)調(diào)用,調(diào)度器可以防止 Goroutine 在進(jìn)行這些系統(tǒng)調(diào)用時(shí)阻塞 M蜕煌。這可以讓 M 執(zhí)行 P 的 LRQ 中其他的 Goroutines派阱,而不需要?jiǎng)?chuàng)建新的 M。有助于減少操作系統(tǒng)上的調(diào)度負(fù)載斜纪。

G1 正在 M 上執(zhí)行贫母,還有 3 個(gè) Goroutine 在 LRQ 上等待執(zhí)行文兑。網(wǎng)絡(luò)輪詢器空閑著,什么都沒(méi)干腺劣。

image

接下來(lái)绿贞,G1 想要進(jìn)行網(wǎng)絡(luò)系統(tǒng)調(diào)用,因此它被移動(dòng)到網(wǎng)絡(luò)輪詢器并且處理異步網(wǎng)絡(luò)系統(tǒng)調(diào)用誓酒。然后樟蠕,M 可以從 LRQ 執(zhí)行另外的 Goroutine贮聂。此時(shí)靠柑,G2 就被上下文切換到 M 上了。

image

最后吓懈,異步網(wǎng)絡(luò)系統(tǒng)調(diào)用由網(wǎng)絡(luò)輪詢器完成歼冰,G1 被移回到 P 的 LRQ 中。一旦 G1 可以在 M 上進(jìn)行上下文切換耻警,它負(fù)責(zé)的 Go 相關(guān)代碼就可以再次執(zhí)行隔嫡。這里的最大優(yōu)勢(shì)是,執(zhí)行網(wǎng)絡(luò)系統(tǒng)調(diào)用不需要額外的 M甘穿。網(wǎng)絡(luò)輪詢器使用系統(tǒng)線程腮恩,它時(shí)刻處理一個(gè)有效的事件循環(huán)。

image

3.當(dāng)調(diào)用一些系統(tǒng)方法的時(shí)候温兼,如果系統(tǒng)方法調(diào)用的時(shí)候發(fā)生阻塞秸滴,這種情況下,網(wǎng)絡(luò)輪詢器(NetPoller)無(wú)法使用募判,而進(jìn)行系統(tǒng)調(diào)用的 Goroutine 將阻塞當(dāng)前 M荡含。

讓我們來(lái)看看同步系統(tǒng)調(diào)用(如文件 I/O)會(huì)導(dǎo)致 M 阻塞的情況:G1 將進(jìn)行同步系統(tǒng)調(diào)用以阻塞 M1。

image

調(diào)度器介入后:識(shí)別出 G1 已導(dǎo)致 M1 阻塞届垫,此時(shí)释液,調(diào)度器將 M1 與 P 分離,同時(shí)也將 G1 帶走装处。然后調(diào)度器引入新的 M2 來(lái)服務(wù) P误债。此時(shí),可以從 LRQ 中選擇 G2 并在 M2 上進(jìn)行上下文切換妄迁。

image

阻塞的系統(tǒng)調(diào)用完成后:G1 可以移回 LRQ 并再次由 P 執(zhí)行寝蹈。如果這種情況再次發(fā)生,M1 將被放在旁邊以備將來(lái)重復(fù)使用判族。

image

4.在Goroutine中去執(zhí)行一個(gè)sleep操作躺盛,導(dǎo)致M被阻塞
Go 程序后臺(tái)有一個(gè)監(jiān)控線程 sysmon,它監(jiān)控那些長(zhǎng)時(shí)間運(yùn)行的 G 任務(wù)然后設(shè)置可以強(qiáng)占的標(biāo)識(shí)符形帮,別的 Goroutine 就可以搶先進(jìn)來(lái)執(zhí)行槽惫。

只要下次這個(gè) Goroutine 進(jìn)行函數(shù)調(diào)用周叮,那么就會(huì)被強(qiáng)占,同時(shí)也會(huì)保護(hù)現(xiàn)場(chǎng)界斜,然后重新放入 P 的本地隊(duì)列里面等待下次執(zhí)行仿耽。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市各薇,隨后出現(xiàn)的幾起案子项贺,更是在濱河造成了極大的恐慌,老刑警劉巖峭判,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件开缎,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡林螃,警方通過(guò)查閱死者的電腦和手機(jī)奕删,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)疗认,“玉大人完残,你說(shuō)我怎么就攤上這事『崧” “怎么了谨设?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)缎浇。 經(jīng)常有香客問(wèn)我扎拣,道長(zhǎng),這世上最難降的妖魔是什么华畏? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任鹏秋,我火速辦了婚禮,結(jié)果婚禮上亡笑,老公的妹妹穿的比我還像新娘侣夷。我一直安慰自己,他們只是感情好仑乌,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布百拓。 她就那樣靜靜地躺著,像睡著了一般晰甚。 火紅的嫁衣襯著肌膚如雪衙传。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天厕九,我揣著相機(jī)與錄音蓖捶,去河邊找鬼。 笑死扁远,一個(gè)胖子當(dāng)著我的面吹牛俊鱼,可吹牛的內(nèi)容都是我干的刻像。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼并闲,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼细睡!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起帝火,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤溜徙,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后犀填,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蠢壹,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年宏浩,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了知残。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片靠瞎。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡比庄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出乏盐,到底是詐尸還是另有隱情佳窑,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布父能,位于F島的核電站神凑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏何吝。R本人自食惡果不足惜溉委,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望爱榕。 院中可真熱鬧瓣喊,春花似錦、人聲如沸黔酥。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)跪者。三九已至棵帽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間渣玲,已是汗流浹背逗概。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留忘衍,地道東北人逾苫。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓指巡,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親隶垮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子藻雪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容