編程模型
在介紹異步模型前狠裹,我們先介紹一下兩種常見的模型榕订。
同步模型
現(xiàn)在有一個(gè)程序要完成三個(gè)毫不相關(guān)的任務(wù)俊鱼。第一種解決方式是采用單線程同步模型蛹磺,如下圖。
這是最簡(jiǎn)單的一種方式瞧哟,每次只執(zhí)行一個(gè)任務(wù)。如果所有任務(wù)總是以確定的順序執(zhí)行枪向,則后面的任務(wù)可以假設(shè)前面的任務(wù)已經(jīng)完成并且沒(méi)有錯(cuò)誤--這個(gè)執(zhí)行邏輯非常簡(jiǎn)單勤揩。
我們可以把這種同步模型和下面的多線程模型比較一下。
多線程模型
在多線程的模型中秘蛔,每個(gè)任務(wù)在單獨(dú)的控制線程中執(zhí)行陨亡。線程由操作系統(tǒng)管理,并且可以在具有多處理器/多核的系統(tǒng)上真正并發(fā)地運(yùn)行深员,或者可以在單個(gè)處理器上交織著運(yùn)行负蠕。這個(gè)模型的關(guān)鍵在于,多線程模型中的執(zhí)行細(xì)節(jié)由操作系統(tǒng)處理倦畅,程序員只需要將任務(wù)看成可以同時(shí)運(yùn)行的指令流就可以了遮糖。雖然模型圖簡(jiǎn)單,但是在實(shí)踐中叠赐,涉及到多線程的程序可能相當(dāng)復(fù)雜欲账,因?yàn)樾枰€程之間彼此協(xié)調(diào)。線程通信和協(xié)調(diào)是一個(gè)高級(jí)編程主題芭概,實(shí)踐起來(lái)很有難度赛不。
有一些程序使用多進(jìn)程而不是多線程來(lái)實(shí)現(xiàn)并行,雖然實(shí)現(xiàn)的細(xì)節(jié)不同罢洲,但是編程模型是一樣的踢故。
異步模型
現(xiàn)在我們來(lái)介紹異步編程模型。
在這個(gè)模型里惹苗,任務(wù)在單個(gè)控制線程中彼此交錯(cuò)執(zhí)行殿较。這個(gè)模型比多線程模型要簡(jiǎn)單,因?yàn)槌绦騿T總是知道在一個(gè)時(shí)間點(diǎn)只有一個(gè)任務(wù)在執(zhí)行鸽粉。雖然在單處理器系統(tǒng)中斜脂,多線程模型的程序也會(huì)以交錯(cuò)的方式執(zhí)行,但是采用多線程的程序員仍然應(yīng)該考慮圖2二不是圖3触机,以免程序在多處理器的系統(tǒng)上不能正確工作帚戳。即使在多處理器系統(tǒng)上玷或,多線程異步程序也始終交織執(zhí)行。
異步模型和多線程模型還有另外一個(gè)區(qū)別片任。在多線程模型中偏友,暫停一個(gè)線程并執(zhí)行另一個(gè)線程的決定大部分在程序員的控制之外。相反对供,這些決定是在操作系統(tǒng)的控制下位他,程序員必須假設(shè)線程可以被掛起并在幾乎任何時(shí)間被另一個(gè)線程替換。相比之下产场,異步模型的程序會(huì)繼續(xù)運(yùn)行鹅髓,直到它顯式地放棄對(duì)其他任務(wù)的控制。這是對(duì)多線程模型的進(jìn)一步簡(jiǎn)化京景。
注意窿冯,可以將異步和多線程模型混合,在同一系統(tǒng)中使用确徙。在對(duì)于這篇介紹中醒串,我們將堅(jiān)持使用具有一個(gè)控制線程的異步系統(tǒng)的“普通原型”。
動(dòng)機(jī)
我們已經(jīng)知道異步模型比多線程模型要簡(jiǎn)單鄙皇,因?yàn)樵谝粋€(gè)指令流中任務(wù)可以顯式放棄控制芜赌,而不是被任意懸掛。但是異步模型顯然比同步模型更復(fù)雜伴逸。程序員必須將每個(gè)任務(wù)組織為間歇執(zhí)行的一系列較小步驟缠沈。如果一個(gè)任務(wù)使用到了另一個(gè)任務(wù)的輸出,則從屬任務(wù)必須將其輸入分成許多小部分违柏,而不是全部一起傳入博烂。由于沒(méi)有實(shí)際的并行性,從我們的圖標(biāo)中可以看出漱竖,異步程序?qū)⑿枰谕匠绦蛞粯娱L(zhǎng)的執(zhí)行時(shí)間禽篱,或許會(huì)需要更長(zhǎng)的時(shí)間,因?yàn)楫惒匠绦蚩赡鼙憩F(xiàn)出較差的參考局部性馍惹。
為什么要選擇使用異步模型呢躺率?至少有兩個(gè)原因。首先万矾,如果一個(gè)或多個(gè)任務(wù)用于實(shí)現(xiàn)與人交互的接口悼吱,通過(guò)將任務(wù)交織在一起,系統(tǒng)可以保持響應(yīng)用戶輸入良狈,同時(shí)仍然在“后臺(tái)”執(zhí)行其他工作后添。因此,雖然后臺(tái)任務(wù)可能不會(huì)執(zhí)行地更快薪丁,但是使用這個(gè)系統(tǒng)的人體驗(yàn)會(huì)更好遇西。
然而馅精,存在這樣一種情況,在該情況下粱檀,如果只考慮任務(wù)總體的執(zhí)行時(shí)間洲敢,異步模型將完全優(yōu)于同步模型。這樣的情況就是任務(wù)被強(qiáng)制等待或者阻塞茄蚯,如圖4所示:
途中的灰色部分表示特定任務(wù)正處于等待(阻塞)狀態(tài)压彭,因此程序會(huì)卡在這里。為什么任務(wù)會(huì)被阻塞呢渗常?常見的原因是它正在等待執(zhí)行I/O壮不,來(lái)向外部設(shè)備傳輸數(shù)據(jù)會(huì)著從外部設(shè)備傳入數(shù)據(jù)。一般CPU處理數(shù)據(jù)傳輸?shù)乃俣缺却疟P或者網(wǎng)卡要快幾個(gè)數(shù)量級(jí)皱碘。因此忆畅,執(zhí)行大量I/O的同步程序?qū)⒒ㄙM(fèi)大部分時(shí)間等待磁盤或者網(wǎng)絡(luò)。由于這個(gè)原因尸执,這種同步程序也被稱為阻塞程序。
注意到圖4中的阻塞程序看起來(lái)有點(diǎn)像圖3中的異步程序缓醋。這并不是偶然的如失。異步模型背后的基本思想是,異步程序在面對(duì)同步程序中通常會(huì)阻塞的任務(wù)時(shí)送粱,會(huì)切換到其他可以繼續(xù)執(zhí)行的任務(wù)褪贵。僅當(dāng)沒(méi)有任何任務(wù)可以執(zhí)行時(shí),異步任務(wù)會(huì)“阻塞”抗俄,因此異步任務(wù)也被稱為非阻塞程序脆丁。當(dāng)一個(gè)任務(wù)完成,或者到達(dá)阻塞的狀態(tài)時(shí)动雹,程序會(huì)切換到另一個(gè)任務(wù)槽卫。當(dāng)存在大量可能會(huì)阻塞的任務(wù)時(shí),異步任務(wù)相比同步任務(wù)胰蝠,總體等待I/O的時(shí)間會(huì)少很多歼培,而真正執(zhí)行任務(wù)的時(shí)間會(huì)大致相等。
相比于同步模型茸塞,滿足下面條件時(shí)躲庄,異步模型性能最好:
- 程序有大量任務(wù),所以總是有至少一個(gè)任務(wù)可以被執(zhí)行
- 任務(wù)會(huì)執(zhí)行大量I/O钾虐,導(dǎo)致同步程序浪費(fèi)大量時(shí)間在阻塞上噪窘,這些時(shí)間本可以用來(lái)執(zhí)行其他任務(wù)
- 任務(wù)之間彼此獨(dú)立,幾乎不需要任務(wù)間通信(一個(gè)任務(wù)等待另外一個(gè)任務(wù))效扫。
在客戶端-服務(wù)器架構(gòu)下繁忙的網(wǎng)絡(luò)服務(wù)器(如web服務(wù)器)幾乎完美地符合上面的條件倔监≈鄙埃客戶端每次發(fā)送請(qǐng)求和接收響應(yīng),都可以看作是一次I/O任務(wù)丐枉,并且客戶的請(qǐng)求之間是互相獨(dú)立的哆键。因此,異步模型主要用于網(wǎng)絡(luò)服務(wù)器的實(shí)現(xiàn)瘦锹。