進程那些事系列-何為協(xié)程

在講協(xié)程前, 我們先點名幾個概念

并行和并發(fā)

摘自知乎的一個例子:

  1. 你正在吃飯, 當(dāng)你電話來了, 你一直到吃完了才去接電話 =》 不支持并發(fā)和并行
  2. 你正在吃飯, 當(dāng)你電話來了, 你放下筷子, 去接電話, 打完后繼續(xù)吃飯 =》支持并發(fā)
  3. 你正在吃飯, 當(dāng)你電話來了, 你一邊接電話, 一邊吃飯 =》 支持并行

并發(fā)和并行都有處理多任務(wù)的能力(吃飯和接電話), 但兩者的區(qū)別在于是否能同時

  • 并發(fā)的關(guān)鍵是你能處理多任務(wù), 但不是同時
  • 并行的關(guān)鍵是你能同時處理多任務(wù), 任務(wù)的最大上限取決于CPU的核數(shù)

用戶態(tài)和內(nèi)核態(tài)

還記得進程結(jié)構(gòu)嗎?


process structure

一個進程實例地址被分成了兩部分, 內(nèi)核空間 和 用戶空間. 在32位的內(nèi)存中, 每個進程最高的1G空間稱自為內(nèi)核空間, 供所有進程共享. 而剩下的3G稱自為用戶空間, 供自己使用

CPU指令集也一樣, CPU的所有指令中, 有些指令是非常危險的, 如果錯用, 將導(dǎo)致系統(tǒng)崩潰(比如,清理內(nèi)存, 設(shè)置時鐘等). 所以我們將指令分成兩個等級: 特權(quán)指令 和 非特權(quán)指令.

無論是內(nèi)核空間還是特權(quán)指令, 都統(tǒng)一交給操作系統(tǒng)的核心 內(nèi)核 來負責(zé), 它獨立于普通應(yīng)用,可以訪問被保護的內(nèi)存空間地址(內(nèi)核地址), 也有訪問底層硬件的權(quán)限.

內(nèi)核態(tài)是提供給內(nèi)核的運行空間, 而用戶態(tài)是提供給應(yīng)用程序運行的空間, 兩者所能使用的指令和內(nèi)存都是不一樣的, 這樣有效地保護了操作系統(tǒng)的安全, 當(dāng)程序崩潰時, 不會影響到操作系統(tǒng)

這里你就會納悶, 好端端的運行程序, 為什么會進入到內(nèi)核態(tài)?
用戶態(tài)進入內(nèi)核態(tài)的三種方式:

  • 系統(tǒng)調(diào)用(System Call)
  • 缺頁中斷(相應(yīng)的虛擬地址沒有映射到物理地址)
  • 中斷(網(wǎng)絡(luò), I/O)

系統(tǒng)調(diào)用相當(dāng)于用戶態(tài)和內(nèi)核態(tài)的中介, 為了使用戶態(tài)的程序能夠訪問到內(nèi)核管理的資源(CPU, 磁盤等), 內(nèi)核提供的一組通用訪問接口, 這些接口就叫做 系統(tǒng)調(diào)用

image.png

當(dāng)用戶態(tài)進入內(nèi)核態(tài) 或者 內(nèi)核態(tài)進入用戶態(tài) 都需要進行上下文(CPU上下文)切換的, 所以一次系統(tǒng)調(diào)用會產(chǎn)生兩次 CPU上下文切換.

協(xié)程

協(xié)程是什么?

協(xié)程, 英文Coroutines, 是一種比線程更輕量級的存在. 正如一個進程可以有多個線程, 一個線程也可以有多個協(xié)程. 它的特性體現(xiàn)在 它不是由操作系統(tǒng)內(nèi)核來管理, 而完全由程序所控制
這點引用特性出了它的核心, 程序所控制. 這會導(dǎo)致在進行協(xié)程切換的時候, 我們不需要從用戶態(tài)到內(nèi)核態(tài), 這無形中就減少了CPU上下文切換所帶來的消耗

盡管線程是輕量級的進程, 它共享著進程的資源, 但它的切換是由內(nèi)核來管理的, 這就意味著每次切換都需要從用戶態(tài)(線程A)到內(nèi)核態(tài), 再從內(nèi)核態(tài)到用戶態(tài)(線程B), 避免不了CPU帶來的消耗

協(xié)程是早年間就有的東西, 但直到現(xiàn)在才開始流行, 這其中的原由離不開 - 高并發(fā)

高并發(fā)下的協(xié)程

隨著互聯(lián)網(wǎng)流量的不斷增大, 請求從百萬到千萬甚至更多. 高并發(fā)下的技術(shù)也一直在演變. 從早期的 多進程 -> 多線程 -> NIO(多路復(fù)用, 異步回調(diào)) -> 協(xié)程
本段參考 05 | 協(xié)程:如何快速地實現(xiàn)高并發(fā)服務(wù)當(dāng)我們在討論高并發(fā)的時候, 在討論著什么, 侵權(quán)則刪

image.png

無論是多線程還是多進程, 當(dāng)我們發(fā)起I/O請求時, 會導(dǎo)致線程的阻塞, 等待請求完成. 這個時候內(nèi)核就會進行線程切換, 等到 I/O 響應(yīng)時, 再切換回來

一說到切換, 開銷就少不了. 特別是面對千萬請求時, 這樣的開銷是機器無法承受的. 人們提出了新的想法, 即如何避免切換?, 或者說如何避免阻塞

image.png

陶老師提到很關(guān)鍵的一點, 把內(nèi)核實現(xiàn)切換的請求工作, 轉(zhuǎn)換到用戶態(tài)來完成.

異步化編程通過一個叫做 Dispatcher 的單線程主循環(huán)(又叫 event loop), 用戶向 Dispatcher 注冊回調(diào)函數(shù), 來實現(xiàn)異步通知, 從而不必在原地干等消費資源. 整個過程由單線程來實現(xiàn), 在Dispatcher主循環(huán)中通過 select()/ epoll() 等系統(tǒng)調(diào)用來等待各種I/O事件的發(fā)生, 并調(diào)用相應(yīng)的回調(diào)函數(shù)(event handle)來處理請求

通過將阻塞方法改造程了非阻塞方法, 和單線程的使用, 將并發(fā)提升到了百萬級請求級別. 但是異步化代碼容易出錯, 而且callback的回調(diào)地獄也讓人頭疼, 阻塞函數(shù)都需要經(jīng)過非阻塞系統(tǒng)拆分成了兩部分, 第一個函數(shù)由你發(fā)起, 第二個函數(shù)由多路復(fù)用機制調(diào)用, 導(dǎo)致代碼太過于復(fù)雜和晦澀

這時候, 人們把目光指向了協(xié)程

A coroutine is a function that can suspend its execution (yield) until the given YieldInstruction finishes.

協(xié)程和異步化編程一樣, 都是通過用戶態(tài)來調(diào)度. 官方的定義中把 協(xié)程比做了函數(shù), 這個函數(shù)在執(zhí)行時, 如果遇到了阻塞, 會使執(zhí)行它的協(xié)程無感知的自動放棄執(zhí)行權(quán), 由協(xié)程框架切換到其他就緒的協(xié)程繼續(xù)執(zhí)行, 當(dāng)結(jié)果滿足后, 該框架選擇合適的時機后切換回它所在的協(xié)程繼續(xù)執(zhí)行

這里又引出了協(xié)程的另一大好處, 即用同步編程的方式來寫高并發(fā)程序

協(xié)程的切換類似于內(nèi)核的切換, 保存兩個最重要的寄存器值(指令寄存器, 棧寄存器). 協(xié)程都有自己的棧, 是從進程的堆中分配的, 很小很小一塊. 這也是為什么說, 一個線程能運行百萬個協(xié)程

協(xié)程的優(yōu)缺點

協(xié)程的優(yōu)點: 我們在總結(jié)下

  • 協(xié)程更輕量, 內(nèi)存消耗下, 創(chuàng)建成本小
  • 協(xié)程大大減少了上下文切換的消耗
  • 減少同步加鎖, 這個是很關(guān)鍵的一點. 因為協(xié)程是協(xié)作式任務(wù)分配, 就自己主動放棄, 讓別人接著運行. 而線程是搶占式的
  • 按照同步的思維來寫異步代碼

缺點:

  • 在協(xié)程中不能有阻塞操作, 否則會導(dǎo)致整個線程阻塞(因為要自己主動放棄, 如果阻塞了, 怎么放棄)
  • 協(xié)程可以處理I/O密集型應(yīng)用, 對CPU密集型不得行(因為CPU密集型一直在算, 哪有時間切換給其他協(xié)程)

總結(jié)

協(xié)程到了就差不大了, 其實主要是理解它的思想和應(yīng)用. 之所以會不斷運用新技術(shù), 也是因為為了更好的壓榨CPU的有效使用

最后, 我們來看看 協(xié)程 和 線程的關(guān)系
這兩者其實并不是互相排斥的, 在很多場景, 都會有 協(xié)程 + 線程的結(jié)合. 比如當(dāng)我們遇到無法將阻塞改成非阻塞狀態(tài)的函數(shù)時, 我們需要線程的調(diào)用. 其次, 無論開多少個協(xié)程, 本質(zhì)上使用的是一個線程, 那用的就是單核CPU, 我們需要借助多線程來使用多核CPU

如果有什么疑問和錯誤, 歡迎指出, 感謝你的支持

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胀溺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌野蝇,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件粘咖,死亡現(xiàn)場離奇詭異讳苦,居然都是意外死亡,警方通過查閱死者的電腦和手機瘫怜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來本刽,“玉大人鲸湃,你說我怎么就攤上這事∽釉ⅲ” “怎么了暗挑?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長斜友。 經(jīng)常有香客問我炸裆,道長,這世上最難降的妖魔是什么鲜屏? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任烹看,我火速辦了婚禮国拇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘惯殊。我一直安慰自己酱吝,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布土思。 她就那樣靜靜地躺著务热,像睡著了一般。 火紅的嫁衣襯著肌膚如雪浪漠。 梳的紋絲不亂的頭發(fā)上陕习,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音址愿,去河邊找鬼该镣。 笑死,一個胖子當(dāng)著我的面吹牛响谓,可吹牛的內(nèi)容都是我干的损合。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼娘纷,長吁一口氣:“原來是場噩夢啊……” “哼嫁审!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起赖晶,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤律适,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后遏插,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捂贿,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年胳嘲,在試婚紗的時候發(fā)現(xiàn)自己被綠了厂僧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡了牛,死狀恐怖颜屠,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鹰祸,我是刑警寧澤甫窟,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站福荸,受9級特大地震影響蕴坪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜敬锐,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一背传、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧台夺,春花似錦径玖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至滚朵,卻和暖如春冤灾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辕近。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工韵吨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人移宅。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓归粉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親漏峰。 傳聞我的和親對象是個殘疾皇子糠悼,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345