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