12 Go 并發(fā)調(diào)度器模型

一、聊聊并發(fā)這件事

在基礎(chǔ)系列我們學(xué)習(xí)了Go的并發(fā)編程田巴,對并發(fā)的概念已經(jīng)有了一定的了解壹哺。在各種現(xiàn)代高級語言中,對并發(fā)的支持已經(jīng)是標(biāo)配截珍,但Go的并發(fā)無論在開發(fā)效率還是在性能上都有相當(dāng)?shù)膬?yōu)越性笛臣。Go有什么獨(dú)特的設(shè)計(jì)讓其在并發(fā)編程領(lǐng)域獨(dú)步江湖沈堡?這得益于Go的并發(fā)調(diào)度器燕雁。

我們知道拐格,軟件在系統(tǒng)運(yùn)行的基本單元是進(jìn)程,由進(jìn)程開辟出多條線程懂衩,而線程則是CPU調(diào)度的基本單位浊洞。我們常說千級并發(fā)法希、萬級并發(fā)靶瘸。甚至百萬級并發(fā),是那么多線程真正同一時(shí)間執(zhí)行嗎屋剑?其實(shí)并不是饼丘,程序同一時(shí)間的并發(fā)數(shù)完全由物理上的CPU核心數(shù)決定肄鸽,即同一時(shí)間單位一個(gè)CPU核心只能執(zhí)行一條線程的計(jì)算,其多線程的并發(fā)完全由調(diào)度器決定蟀苛。

二帜平、并發(fā)模型概述

各種高級語言的調(diào)度器設(shè)計(jì)各不相同裆甩,但基本可以分成三種模型設(shè)計(jì)齐唆,第一種為內(nèi)核級并發(fā)模型(1:1)箍邮,即內(nèi)核線程和用戶線程綁定;第二種為用戶級并發(fā)模型(N:1)堪澎,由用戶維護(hù)線程的管理樱蛤,其內(nèi)核線程和用戶線程的調(diào)度由調(diào)度器實(shí)現(xiàn)刹悴;第三種為兩級調(diào)度的混合并發(fā)模型(N:M),這種集前兩種模型的優(yōu)勢磕道,完全由語言運(yùn)行時(shí)的調(diào)度器控制概作,是實(shí)現(xiàn)最為復(fù)雜的一種田度。下面我們分別了解一下這三種并發(fā)模型:

1.內(nèi)核級并發(fā)模型(1:1)

所謂內(nèi)核線程镇饺,即物理線程,可以被操作系統(tǒng)內(nèi)核調(diào)度器調(diào)度的對象實(shí)體惋啃,是操作系統(tǒng)內(nèi)核的最小調(diào)度單元边灭。這里用戶開辟的每一個(gè)線程和內(nèi)核線程綁定(1:1)健盒,線程的調(diào)度完全由系統(tǒng)內(nèi)核管理,應(yīng)用程序?qū)€程幾乎沒有管理惰帽,大部分語言的線程庫该酗,如JAVA频轿,都是對操作系統(tǒng)內(nèi)核線程的封裝。

優(yōu)點(diǎn):

  • 實(shí)現(xiàn)簡單耕赘,直接扔給操作系統(tǒng)內(nèi)核管理操骡;
  • 線程切換由CPU實(shí)現(xiàn)赚窃,可以有效利用CPU的多核;

缺點(diǎn):

  • CPU管理的線程切換涉及線程的上下文是掰、資源調(diào)度键痛,因此切換成本很高;
  • 不宜開辟過多的并發(fā)量絮短,內(nèi)核線程的堆棧空間在 Windows 下默認(rèn) 2M杉允,Linux 下默認(rèn) 8M叔磷,過多的并發(fā)量導(dǎo)致的切換開銷對性能影響是很大的胁勺。

2.用戶級并發(fā)模型(N:1)

所謂用戶級并發(fā)模型,是指單個(gè)進(jìn)程內(nèi)部管理的多條線程寥裂,線程的調(diào)度完全由用戶進(jìn)程決定封恰,一個(gè)進(jìn)程中的所有線程都只和一個(gè)CPU內(nèi)核線程在運(yùn)行時(shí)動(dòng)態(tài)綁定(N:1)褐啡。即CPU內(nèi)核線程的調(diào)度器對用戶進(jìn)程內(nèi)部的多條線程是無感知的,內(nèi)核線程只知道用戶進(jìn)程低飒。像python褥赊、nodejs等語言的并發(fā)實(shí)現(xiàn)就是這種模型莉恼,簡單來說這是一種偽并發(fā),因?yàn)橥粫r(shí)間內(nèi)允許的線程只有一個(gè)尿背。

優(yōu)點(diǎn):

  • 線程調(diào)度在用戶層面田藐,CPU不需要在用戶態(tài)和內(nèi)核態(tài)切換,資源開銷很熊罱;
  • 由于沒有上下文切換帶來的開銷市袖,其用戶線程比較輕量化,可以開啟較多的用戶線程酒觅。

缺點(diǎn):

  • 并不能做到真正意義上的并發(fā)舷丹,其本質(zhì)還是單核計(jì)算蜓肆,對于IO阻塞的任務(wù)還是會被中斷;
  • 需要線程庫把IO阻塞的操作重新封裝為完全的非阻塞形式症概,然后在以前要阻塞的點(diǎn)上彼城,主動(dòng)讓出自己退个,并通過某種方式通知或喚醒其他待執(zhí)行的用戶線程在內(nèi)核線程上運(yùn)行
調(diào)度器模型1.jpg

3.混合型并發(fā)模型(N:M)

以上兩種調(diào)度器模型都有優(yōu)缺點(diǎn)语盈,有沒有可能取長補(bǔ)短設(shè)計(jì)新的調(diào)度器模型呢?混合型并發(fā)模型就是博采眾長之后的產(chǎn)物习柠,充分吸收前兩種線程模型的優(yōu)點(diǎn)且盡量規(guī)避它們的缺點(diǎn)资溃×姨浚混合型并發(fā)模型是讓內(nèi)核線程和用戶線程建立多對多的關(guān)系(N:M),即一個(gè)用戶進(jìn)程可以和多個(gè)內(nèi)核線程關(guān)聯(lián)趴捅,相當(dāng)于用戶進(jìn)程內(nèi)部開辟的多個(gè)用戶線程可以動(dòng)態(tài)綁定多個(gè)內(nèi)核線程。這種模型避免了內(nèi)核級模型中的完全靠操作系統(tǒng)調(diào)度的并發(fā)性能問題综芥,也避免了用戶級模型中的偽并發(fā)問題猎拨,它是用戶自身調(diào)度器和系統(tǒng)調(diào)度器協(xié)同工作的設(shè)計(jì)红省。

優(yōu)點(diǎn):

  • 充分博采眾長,實(shí)現(xiàn)真正意義上的高效能并發(fā)虾啦;
  • 對用戶友好痕寓,用戶只需管理業(yè)務(wù)層面的并發(fā)任務(wù)。

缺點(diǎn):

  • 由于這種模型需用用戶級調(diào)度和系統(tǒng)級調(diào)度協(xié)同工作需频,所以這種調(diào)度器實(shí)現(xiàn)都相當(dāng)復(fù)雜昭殉。
調(diào)度器模型2.jpg

三挪丢、Go并發(fā)調(diào)度器解析

Go調(diào)度器中的三種結(jié)構(gòu)G卢厂、P、M

系統(tǒng)線程固定2M任内,且維護(hù)一堆上下文融柬,對需求多變的并發(fā)應(yīng)用并不友好粒氧,有可能造成內(nèi)存浪費(fèi)或內(nèi)存不夠用。Go將并發(fā)的單位下降到線程以下摘盆,由其設(shè)計(jì)的goroutine初始空間非常小,僅2kb狼渊,但支持動(dòng)態(tài)擴(kuò)容到最大1G类垦,這就是go自己的并發(fā)單元——goroutine協(xié)程护锤。

實(shí)際上系統(tǒng)最小的執(zhí)行單元仍然是線程烙懦,go運(yùn)行時(shí)執(zhí)行的協(xié)程也是掛載到某一系統(tǒng)線程之上的赤炒,這種協(xié)程與系統(tǒng)線程的調(diào)度分配由Go的并發(fā)調(diào)度器承擔(dān),Go的并發(fā)調(diào)度器是屬于混合的二級調(diào)度并發(fā)模型掩缓,其內(nèi)部設(shè)計(jì)有G你辣、P尘执、M三種抽象結(jié)構(gòu),我們來看一下它們分別是什么:

G-P-M模型抽象結(jié)構(gòu):

  • G: 表示Goroutine表悬,每個(gè)Goroutine對應(yīng)一個(gè)G結(jié)構(gòu)體蟆沫,G存儲Goroutine的運(yùn)行堆棧温治、狀態(tài)以及任務(wù)函數(shù),可重用但绕。G運(yùn)行隊(duì)列是一個(gè)棧結(jié)構(gòu),分全局隊(duì)列和P綁定的局部隊(duì)列六孵,每個(gè)G不能獨(dú)立運(yùn)行劫窒,它需要綁定到P才能被調(diào)度執(zhí)行拆座。
  • P: Processor,表示邏輯處理器孕索, 對G來說搞旭,P相當(dāng)于CPU核菇绵,G只有綁定到P(在P的local runq中)才能被調(diào)度。對M來說翎嫡,P提供了相關(guān)的執(zhí)行環(huán)境(Context)永乌,如內(nèi)存分配狀態(tài)(mcache)翅雏,任務(wù)隊(duì)列(G)等,P的數(shù)量決定了系統(tǒng)內(nèi)最大可并行的G的數(shù)量(前提:物理CPU核數(shù) >= P的數(shù)量)碗脊,P的數(shù)量由用戶設(shè)置的GOMAXPROCS決定橄妆,但是不論GOMAXPROCS設(shè)置為多大,P的數(shù)量最大為256矢劲。
  • M: Machine芬沉,系統(tǒng)物理線程,代表著真正執(zhí)行計(jì)算的資源丸逸,在綁定有效的P后,進(jìn)入schedule循環(huán)捎谨;而schedule循環(huán)的機(jī)制大致是從Global隊(duì)列涛救、P的Local隊(duì)列以及wait隊(duì)列中獲取G业扒,切換到G的執(zhí)行棧上并執(zhí)行G的函數(shù),調(diào)用goexit做清理工作并回到M咧栗,如此反復(fù)。M并不保留G狀態(tài)交煞,這是G可以跨M調(diào)度的基礎(chǔ),M的數(shù)量是不定的集嵌,由Go Runtime調(diào)整御毅,為了防止創(chuàng)建過多OS線程導(dǎo)致系統(tǒng)調(diào)度不過來端蛆,目前默認(rèn)最大限制為10000個(gè)。

關(guān)于P這個(gè)設(shè)計(jì)嫌拣,是在Go1.0之后才實(shí)現(xiàn)的呆躲,起初的Go并發(fā)性能并不十分亮眼,協(xié)程和系統(tǒng)線程的調(diào)度比較粗暴灰瞻,導(dǎo)致很多性能問題,如全局資源鎖燎竖、M的內(nèi)存過高等造成許多性能損耗袍祖,加入P的設(shè)計(jì)后實(shí)現(xiàn)了一個(gè)叫做 work-stealing 的調(diào)度算法:由P來維護(hù)Goroutine隊(duì)列并選擇一個(gè)適當(dāng)?shù)腗綁定。

Go并發(fā)調(diào)度器的GPM模型.jpg

G-P-M模型調(diào)度

我們來看看go關(guān)鍵字創(chuàng)建一個(gè)協(xié)程后其調(diào)度器是怎么工作的:

  • go關(guān)鍵字創(chuàng)建goroutine(G)捐凭,優(yōu)先加入某個(gè)P維護(hù)的局部隊(duì)列(當(dāng)局部隊(duì)列已滿時(shí)才加入全局隊(duì)列)茁肠;
  • P需要持有或者綁定一個(gè)M垦梆,而M會啟動(dòng)一個(gè)系統(tǒng)線程仅孩,不斷的從P的本地隊(duì)列取出G并執(zhí)行;
  • M執(zhí)行完P(guān)維護(hù)的局部隊(duì)列后京腥,它會嘗試從全局隊(duì)列尋找G公浪,如果全局隊(duì)列為空船侧,則從其他的P維護(hù)的隊(duì)列里竊取一般的G到自己的隊(duì)列;
  • 重復(fù)以上知道所有的G執(zhí)行完畢预柒。

當(dāng)然也有一些情況會造成Goroutine阻塞袁梗,如:

  • 系統(tǒng)GC围段;
  • 系統(tǒng)IO資源的調(diào)用,如文件讀寫适贸;
  • 網(wǎng)絡(luò)IO的延遲;
  • 管道阻塞拜姿;
  • 同步操作。

當(dāng)遇到上述阻塞時(shí)谒获,Go調(diào)度器也有相應(yīng)的處理方式:

  • 1.系統(tǒng)調(diào)度引起阻塞:

如系統(tǒng)GC批狱,M會解綁P展东,出讓控制權(quán)給其他M,讓該P(yáng)維護(hù)的G運(yùn)行隊(duì)列不至于阻塞爪膊。

  • 2.用戶態(tài)的阻塞:

當(dāng)goroutine因?yàn)楣艿啦僮骰蛘呦到y(tǒng)IO推盛、網(wǎng)絡(luò)IO而阻塞時(shí)谦铃,對應(yīng)的G會被放置到某個(gè)等待隊(duì)列,該G的狀態(tài)由運(yùn)行時(shí)變?yōu)榈却隣顟B(tài),而M會跳過該G嘗試獲取并執(zhí)行下一個(gè)G件豌,如果此時(shí)沒有可運(yùn)行的G供M運(yùn)行茧彤,那么M將解綁P,并進(jìn)入休眠狀態(tài)惫谤;當(dāng)阻塞的G被另一端的G2喚醒時(shí)珠洗,如管道通知,G又被標(biāo)記為可運(yùn)行狀態(tài)蝴猪,嘗試加入G2所在P局部隊(duì)列的隊(duì)頭,然后再是G全局隊(duì)列自阱。

  • 3.當(dāng)存在空閑的P時(shí),竊取其他隊(duì)列的G:

當(dāng)P維護(hù)的局部隊(duì)列全部運(yùn)行完畢趋箩,它會嘗試在全局隊(duì)列獲取G叫确,直到全局隊(duì)列為空哼丈,再向其他局部隊(duì)列竊取一般的G。

至此Go的調(diào)度器模型解析完畢饶米〕岛基于Go調(diào)度器的優(yōu)越設(shè)計(jì),它號稱能實(shí)現(xiàn)百萬級并發(fā)匈棘,即使日常很難達(dá)到這種并發(fā)量主卫,我們也應(yīng)該對并發(fā)的使用要心存敬畏,真正的并發(fā)依賴于物理核心完域,啟動(dòng)并發(fā)是需要系統(tǒng)開銷的瘩将,雖然在Go的運(yùn)行時(shí)它看起來很小,但量變引起質(zhì)變肠仪,當(dāng)業(yè)務(wù)啟動(dòng)的并發(fā)到十萬級备典、百萬級甚至千萬級時(shí)提佣,其性能開銷還是非常巨大的欲险∑ヤ蹋可以通過一定的手段控制并發(fā)數(shù)量以防止系統(tǒng)奔潰,如實(shí)現(xiàn)一個(gè)協(xié)程池喜每,通過worker機(jī)制控制并發(fā)數(shù)带兜。

Ok吨灭,希望學(xué)完這一專題你會對Go的并發(fā)有更深刻的了解。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末无畔,一起剝皮案震驚了整個(gè)濱河市浑彰,隨后出現(xiàn)的幾起案子拯辙,更是在濱河造成了極大的恐慌,老刑警劉巖诉濒,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件未荒,死亡現(xiàn)場離奇詭異撇他,居然都是意外死亡困肩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進(jìn)店門锌畸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來潭枣,“玉大人,你說我怎么就攤上這事盆犁⌒乘辏” “怎么了?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵窜司,是天一觀的道長航揉。 經(jīng)常有香客問我,道長议薪,這世上最難降的妖魔是什么漠秋? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任庆锦,我火速辦了婚禮,結(jié)果婚禮上艇搀,老公的妹妹穿的比我還像新娘求晶。我一直安慰自己,他們只是感情好矩屁,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布吝秕。 她就那樣靜靜地躺著空幻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪缩挑。 梳的紋絲不亂的頭發(fā)上鬓梅,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天绽快,我揣著相機(jī)與錄音,去河邊找鬼娄柳。 笑死赤拒,一個(gè)胖子當(dāng)著我的面吹牛诱鞠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播航夺,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼始衅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了汛闸?” 一聲冷哼從身側(cè)響起诸老,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤钳恕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后厘肮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體轴脐,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡大咱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年碴巾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了丑搔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,643評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡煮仇,死狀恐怖浙垫,靈堂內(nèi)的尸體忽然破棺而出郑诺,到底是詐尸還是另有隱情辙诞,我是刑警寧澤,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布旦部,位于F島的核電站志鹃,受9級特大地震影響泽西,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜陕见,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一评甜、第九天 我趴在偏房一處隱蔽的房頂上張望忍坷。 院中可真熱鬧,春花似錦、人聲如沸佩研。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽旬薯。三九已至晰骑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間绊序,已是汗流浹背硕舆。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留骤公,地道東北人抚官。 一個(gè)月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像阶捆,于是被迫代替她去往敵國和親刊咳。 傳聞我的和親對象是個(gè)殘疾皇子娱挨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評論 2 348

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

  • 本篇文章是我對 Go 語言并發(fā)性的理解總結(jié)碉碉,適合初步了解并發(fā),對 Go 語言的并發(fā)編程與調(diào)度器原理有興趣的讀者贴届。 ...
    baiyi閱讀 1,229評論 0 4
  • 理解并發(fā)和并行并發(fā):同時(shí)管理多件事情昔善。并行:同時(shí)做多件事情。表示同時(shí)發(fā)生了多件事情翩概,通過時(shí)間片切換牍鞠,哪怕只有單一的...
    Chuck_Hu閱讀 6,036評論 7 44
  • 閱讀Go并發(fā)編程對go語言線程模型的筆記楞艾,解釋的非常到硫眯,好記性不如爛筆頭两入,忘記的時(shí)候回來翻一番裹纳,在此做下筆記剃氧。 G...
    WithLin閱讀 1,139評論 0 4
  • 1. C/C++ 與 Go語言的“價(jià)值觀”對照 C的價(jià)值觀摘錄 相信程序員:提供指針和指針運(yùn)算,讓C程序員天馬行空...
    ywhu閱讀 6,894評論 0 13
  • 今天是大年初三,好像年味也一下結(jié)束似的缆蝉,我在恍惚中總好像感覺不到自己在“過年”當(dāng)中瘦真。昨天照舊是回鄉(xiāng)芽偏,今天照舊是在姨...
    安靈子閱讀 414評論 0 0