最近有人在Twisted郵件列表中提出諸如"為任務(wù)緊急的人提供一份Twisted介紹"的需求。值得提前透露的是,這個(gè)系列并不會(huì)如他們所愿。尤其是介紹Twisted框架和基于Python 的異步編程而言,可能短時(shí)間無(wú)法講清楚。因此,如果你時(shí)間緊急,這恐怕不是你想找的資料。
我相信如果對(duì)異步編程模型一無(wú)所知,快速的介紹同樣無(wú)法讓你對(duì)其有所理解,至少你得稍微懂點(diǎn)基礎(chǔ)知識(shí)吧。我已經(jīng)用Twisted框架幾年了,因此思考過(guò)我當(dāng)初是怎么學(xué)習(xí)它(學(xué)得很慢)并發(fā)現(xiàn)學(xué)習(xí)它的最大難度并不在Twisted本身,而在于對(duì)其模型的理解,只有理解了這個(gè)模型,你才能更好去寫(xiě)和理解異步程序的代碼。大部分Twisted的代碼寫(xiě)得很清晰,其在線(xiàn)文檔也非常棒(至少在開(kāi)源軟件這個(gè)層次上可以這么說(shuō))。但如果不理解這個(gè)模型,不管是讀Twisted源碼還是使用Twisted的代碼更或者是相關(guān)文檔,你都會(huì)感到非常的傷腦筋。
因此,我會(huì)用前面幾個(gè)部分來(lái)介紹這個(gè)模型以讓你掌握它的機(jī)制,稍后會(huì)介紹一下Twisted的特點(diǎn)。實(shí)際上,一開(kāi)始,我們并不會(huì)使用Twisted,相反,會(huì)使用簡(jiǎn)單的Python代碼來(lái)說(shuō)明一個(gè)異步模型是如何工作的。我們?cè)诔醮螌W(xué)習(xí)Twisted的時(shí),會(huì)從你平常都不會(huì)直接使用的底層的實(shí)現(xiàn)講起。Twisted是一個(gè)高度抽象的體系,因此在使用它時(shí),你會(huì)體會(huì)到其多層次性。但當(dāng)你去學(xué)習(xí)尤其是嘗試著理解它是如何工作時(shí),這種為抽像而帶來(lái)的多層次性會(huì)給你帶來(lái)極大的理解難度。所以,我們準(zhǔn)備來(lái)個(gè)從內(nèi)到外,從低層開(kāi)始學(xué)習(xí)它。
為了更好的理解異步編程模型的特點(diǎn),我們來(lái)回顧一下兩個(gè)大家都熟悉的模型。在闡述過(guò)程中,我們假設(shè)一個(gè)包含三個(gè)相互獨(dú)立任務(wù)的程序。在此,除了規(guī)定這些任務(wù)都要完成自己工作外,我們先不作具體的解釋?zhuān)竺嫖覀儠?huì)慢慢具體了解它們。請(qǐng)注意:在此我用"任務(wù)"這個(gè)詞,這意味著它需要完成一些事情。
第一個(gè)模型是單線(xiàn)程的同步模型,如圖1所示:
http://wiki.jikexueyuan.com/project/twisted-intro/images/p01_sync.png" alt="" />
這是最簡(jiǎn)單的編程方式。在一個(gè)時(shí)刻,只能有一個(gè)任務(wù)在執(zhí)行,并且前一個(gè)任務(wù)結(jié)束后一個(gè)任務(wù)才能開(kāi)始。如果任務(wù)都能按照事先規(guī)定好的順序執(zhí)行,最后一個(gè)任務(wù)的完成意味著前面所有的任務(wù)都已無(wú)任何差錯(cuò)地完成并輸出其可用的結(jié)果—這是多么簡(jiǎn)單的邏輯。 下面我們來(lái)呈現(xiàn)第二個(gè)模型,如圖2所示:
http://wiki.jikexueyuan.com/project/twisted-intro/images/p01_threaded.png" alt="" />
在這個(gè)模型中,每個(gè)任務(wù)都在單獨(dú)的線(xiàn)程中完成。這些線(xiàn)程都是由操作系統(tǒng)來(lái)管理,若在多處理機(jī)、多核處理機(jī)的系統(tǒng)中可能會(huì)相互獨(dú)立的運(yùn)行,若在單處理機(jī)上,則會(huì)交錯(cuò)運(yùn)行。關(guān)鍵點(diǎn)在于,在線(xiàn)程模式中,具體哪個(gè)任務(wù)執(zhí)行由操作系統(tǒng)來(lái)處理。但編程人員則只需簡(jiǎn)單地認(rèn)為:它們的指令流是相互獨(dú)立且可以并行執(zhí)行。雖然,從圖示看起來(lái)很簡(jiǎn)單,實(shí)際上多線(xiàn)程編程是很麻煩的,你想啊,任務(wù)之間的要通信就要是線(xiàn)程之間的通信。線(xiàn)程間的通信那不是一般的復(fù)雜。什么郵槽、通道、共享內(nèi)存。。。 唉—__-
一些程序用多處理機(jī)而不是多線(xiàn)程來(lái)實(shí)現(xiàn)并行運(yùn)算。雖然具體的編程細(xì)節(jié)是不同的,但對(duì)于我們要研究的模型來(lái)說(shuō)是一樣的。
下面我們來(lái)介紹一下異步編程模型,如圖3所示
http://wiki.jikexueyuan.com/project/twisted-intro/images/p01_async.png" alt="" />
在這個(gè)模型中,任務(wù)是交錯(cuò)完成,值得注意的是:這是在單線(xiàn)程的控制下。這要比多線(xiàn)程模型簡(jiǎn)單多了,因?yàn)榫幊倘藛T總可以認(rèn)為只有一個(gè)任務(wù)在執(zhí)行,而其它的在停止?fàn)顟B(tài)。雖然在單處理機(jī)系統(tǒng)中,線(xiàn)程也是像圖3那樣交替進(jìn)行。但作為程序員在使用多線(xiàn)程時(shí),仍然需要使用圖2而不是圖3的來(lái)思考問(wèn)題,以防止程序在挪到多處理機(jī)的系統(tǒng)上無(wú)法正常運(yùn)行(考慮到兼容性)。但單線(xiàn)程的異步程序不管是在單處理機(jī)還是在多處理機(jī)上都能很好的運(yùn)行。
在異步編程模型與多線(xiàn)程模型之間還有一個(gè)不同:在多線(xiàn)程程序中,對(duì)于停止某個(gè)線(xiàn)程啟動(dòng)另外一個(gè)線(xiàn)程,其決定權(quán)并不在程序員手里而在操作系統(tǒng)那里,因此,程序員在編寫(xiě)程序過(guò)程中必須要假設(shè)在任何時(shí)候一個(gè)線(xiàn)程都有可能被停止而啟動(dòng)另外一個(gè)線(xiàn)程。相反,在異步模型中,一個(gè)任務(wù)要想運(yùn)行必須顯式放棄當(dāng)前運(yùn)行的任務(wù)的控制權(quán)。這也是相比多線(xiàn)程模型來(lái)說(shuō),最簡(jiǎn)潔的地方。 值得注意的是:將異步編程模型與同步模型混合在同一個(gè)系統(tǒng)中是可以的。但在介紹中的絕大多數(shù)時(shí)候,我們只研究在單個(gè)線(xiàn)程中的異步編程模型。
我們已經(jīng)看到異步編程模型之所以比多線(xiàn)程模型簡(jiǎn)單在于其單指令流與顯式地放棄對(duì)任務(wù)的控制權(quán)而不是被操作系統(tǒng)隨機(jī)地停止。但是異步模型要比同步模型復(fù)雜得多。程序員必須將任務(wù)組織成序列來(lái)交替的小步完成。因此,若其中一個(gè)任務(wù)用到另外一個(gè)任務(wù)的輸出,則依賴(lài)的任務(wù)(即接收輸出的任務(wù))需要被設(shè)計(jì)成為要接收系列比特或分片而不是一下全部接收。由于沒(méi)有實(shí)質(zhì)上的并行,從我們的圖中可以看出,一個(gè)異步程序會(huì)花費(fèi)一個(gè)同步程序所需要的時(shí)間,可能會(huì)由于異步程序的性能問(wèn)題而花費(fèi)更長(zhǎng)的時(shí)間。
因此,就要問(wèn)了,為什么還要使用異步模型呢? 在這兒,我們至少有兩個(gè)原因。首先,如果有一到兩個(gè)任務(wù)需要完成面向人的接口,如果交替執(zhí)行這些任務(wù),系統(tǒng)在保持對(duì)用戶(hù)響應(yīng)的同時(shí)在后臺(tái)執(zhí)行其它的任務(wù)。因此,雖然后臺(tái)的任務(wù)可能不會(huì)運(yùn)行的更快,但這樣的系統(tǒng)可能會(huì)受歡迎的多。
然而,有一種情況下,異步模型的性能會(huì)高于同步模型,有時(shí)甚至?xí)浅M怀?,即在比較短的時(shí)間內(nèi)完成所有的任務(wù)。這種情況就是任務(wù)被強(qiáng)行等待或阻塞,如圖4所示:
http://wiki.jikexueyuan.com/project/twisted-intro/images/p01_block.png" alt="" />
在圖4中,灰色的部分代表這段時(shí)間某個(gè)任務(wù)被阻塞。為什么要阻塞一個(gè)任務(wù)呢?最直接的原因就是等待I/O的完成:傳輸數(shù)據(jù)或來(lái)自某個(gè)外部設(shè)備。一個(gè)典型的CPU處理數(shù)據(jù)的能力是硬盤(pán)或網(wǎng)絡(luò)的幾個(gè)數(shù)量級(jí)的倍數(shù)。因此,一個(gè)需要進(jìn)行大I/O操作的同步程序需要花費(fèi)大量的時(shí)間等待硬盤(pán)或網(wǎng)絡(luò)將數(shù)據(jù)準(zhǔn)備好。正是由于這個(gè)原因,同步程序也被稱(chēng)作為阻塞程序。
從圖4中可以看出,一個(gè)可阻塞的程序,看起來(lái)與圖3描述的異步程序有點(diǎn)像。這不是個(gè)巧合。異步程序背后的最主要的特點(diǎn)就在于,當(dāng)出現(xiàn)一個(gè)任務(wù)像在同步程序一樣出現(xiàn)阻塞時(shí),會(huì)讓其它可以執(zhí)行的任務(wù)繼續(xù)執(zhí)行,而不會(huì)像同步程序中那樣全部阻塞掉。因此一個(gè)異步程序只有在沒(méi)有任務(wù)可執(zhí)行時(shí)才會(huì)出現(xiàn)"阻塞",這也是為什么異步程序被稱(chēng)為非阻塞程序的原因。 任務(wù)之間的切換要不是此任務(wù)完成,要不就是它被阻塞。由于大量任務(wù)可能會(huì)被阻塞,異步程序等待的時(shí)間少于同步程序而將這些時(shí)間用于其它實(shí)時(shí)工作的處理(如與人打交道的接口),這樣一來(lái),前者的性能必然要高很多。
與同步模型相比,異步模型的優(yōu)勢(shì)在如下情況下會(huì)得到發(fā)揮:
這些條件大多在CS模式中的網(wǎng)絡(luò)比較繁忙的服務(wù)器端出現(xiàn)(如WEB服務(wù)器)。每個(gè)任務(wù)代表一個(gè)客戶(hù)端進(jìn)行接收請(qǐng)求并回復(fù)的I/O操作??蛻?hù)的請(qǐng)求(相當(dāng)于讀操作)都是相互獨(dú)立的。因此一個(gè)網(wǎng)絡(luò)服務(wù)是異步模型的典型代表,這也是為什么twisted是第一個(gè)也是最棒的網(wǎng)絡(luò)庫(kù)。
本部分原作參見(jiàn): dave http://krondo.com/?p=1209
本部分翻譯內(nèi)容參見(jiàn)楊曉偉的博客 http://blog.sina.com.cn/s/blog_704b6af70100py9f.html