欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python的Twisted框架上手前所必須了解的異步編程思想

 更新時(shí)間:2016年05月25日 10:42:10   投稿:goldensun  
Twisted是Python世界中人氣最高的framework之一,異步的工作模式使其名揚(yáng)天下,這里為大家總結(jié)了Python的Twisted框架上手前所必須了解的異步編程思想,需要的朋友可以參考下

前言
最近有人在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ě)得很清晰,其在線文檔也非常棒(至少在開(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è)模型是單線程的同步模型,如圖1所示:

2016525102712530.png (136×361)

圖1 同步模型
這是最簡(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所示:

2016525102742407.png (378×120)

圖2 線程模型
在這個(gè)模型中,每個(gè)任務(wù)都在單獨(dú)的線程中完成。這些線程都是由操作系統(tǒng)來(lái)管理,若在多處理機(jī)、多核處理機(jī)的系統(tǒng)中可能會(huì)相互獨(dú)立的運(yùn)行,若在單處理機(jī)上,則會(huì)交錯(cuò)運(yùn)行。關(guān)鍵點(diǎn)在于,在線程模式中,具體哪個(gè)任務(wù)執(zhí)行由操作系統(tǒng)來(lái)處理。但編程人員則只需簡(jiǎn)單地認(rèn)為:它們的指令流是相互獨(dú)立且可以并行執(zhí)行。雖然,從圖示看起來(lái)很簡(jiǎn)單,實(shí)際上多線程編程是很麻煩的,你想啊,任務(wù)之間的要通信就要是線程之間的通信。線程間的通信那不是一般的復(fù)雜。什么郵槽、通道、共享內(nèi)存。。。 唉—__-
一些程序用多處理機(jī)而不是多線程來(lái)實(shí)現(xiàn)并行運(yùn)算。雖然具體的編程細(xì)節(jié)是不同的,但對(duì)于我們要研究的模型來(lái)說(shuō)是一樣的。
下面我們來(lái)介紹一下異步編程模型,如圖3所示

2016525103020951.png (176×361)

圖3 異步模型
在這個(gè)模型中,任務(wù)是交錯(cuò)完成,值得注意的是:這是在單線程的控制下。這要比多線程模型簡(jiǎn)單多了,因?yàn)榫幊倘藛T總可以認(rèn)為只有一個(gè)任務(wù)在執(zhí)行,而其它的在停止?fàn)顟B(tài)。雖然在單處理機(jī)系統(tǒng)中,線程也是像圖3那樣交替進(jìn)行。但作為程序員在使用多線程時(shí),仍然需要使用圖2而不是圖3的來(lái)思考問(wèn)題,以防止程序在挪到多處理機(jī)的系統(tǒng)上無(wú)法正常運(yùn)行(考慮到兼容性)。但單線程的異步程序不管是在單處理機(jī)還是在多處理機(jī)上都能很好的運(yùn)行。
在異步編程模型與多線程模型之間還有一個(gè)不同:在多線程程序中,對(duì)于停止某個(gè)線程啟動(dòng)另外一個(gè)線程,其決定權(quán)并不在程序員手里而在操作系統(tǒng)那里,因此,程序員在編寫(xiě)程序過(guò)程中必須要假設(shè)在任何時(shí)候一個(gè)線程都有可能被停止而啟動(dòng)另外一個(gè)線程。相反,在異步模型中,一個(gè)任務(wù)要想運(yùn)行必須顯式放棄當(dāng)前運(yùn)行的任務(wù)的控制權(quán)。這也是相比多線程模型來(lái)說(shuō),最簡(jiǎn)潔的地方。 值得注意的是:將異步編程模型與同步模型混合在同一個(gè)系統(tǒng)中是可以的。但在介紹中的絕大多數(shù)時(shí)候,我們只研究在單個(gè)線程中的異步編程模型。
動(dòng)機(jī)
我們已經(jīng)看到異步編程模型之所以比多線程模型簡(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ì)用戶響應(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所示:

2016525103044987.png (257×361)

圖4 同步模型中出現(xiàn)阻塞
在圖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ā)揮:
有大量的任務(wù),以至于可以認(rèn)為在一個(gè)時(shí)刻至少有一個(gè)任務(wù)要運(yùn)行
任務(wù)執(zhí)行大量的I/O操作,這樣同步模型就會(huì)在因?yàn)槿蝿?wù)阻塞而浪費(fèi)大量的時(shí)間
任務(wù)之間相互獨(dú)立,以至于任務(wù)內(nèi)部的交互很少。
這些條件大多在CS模式中的網(wǎng)絡(luò)比較繁忙的服務(wù)器端出現(xiàn)(如WEB服務(wù)器)。每個(gè)任務(wù)代表一個(gè)客戶端進(jìn)行接收請(qǐng)求并回復(fù)的I/O操作。客戶的請(qǐng)求(相當(dāng)于讀操作)都是相互獨(dú)立的。因此一個(gè)網(wǎng)絡(luò)服務(wù)是異步模型的典型代表,這也是為什么twisted是第一個(gè)也是最棒的網(wǎng)絡(luò)庫(kù)。

對(duì)你的假設(shè)
在展開(kāi)討論前,我假設(shè)你已經(jīng)有過(guò)用Python寫(xiě)同步程序的經(jīng)歷并且至少知道一點(diǎn)有關(guān)Python的Sockt編程的經(jīng)驗(yàn)。如果你從沒(méi)有寫(xiě)過(guò)Socket程序,或許你可以去看看Socket模塊的文檔,尤其是后面的示例代碼。如果你沒(méi)有用過(guò)Python的話,那后面的描述對(duì)你來(lái)說(shuō)可能比看周易還痛苦。

對(duì)你的環(huán)境假設(shè)
我一般是在Linux上使用Twisted,這個(gè)系列的示例代碼也是在Linux下完成的。首先聲明的是我并沒(méi)有故意讓代碼失去平臺(tái)無(wú)關(guān)性,但我所講述的一些內(nèi)容確實(shí)可能僅僅適應(yīng)于Linux和其它的類(lèi)Unix(比如MAC OSX或FreeBSD)。WIndows是個(gè)奇怪詭異的地方(為什么這么評(píng)價(jià)Windows呢),如果你想嘗試在它上面學(xué)習(xí)這個(gè)系列,抱歉,如果出了問(wèn)題,我無(wú)法提供任何幫助。 并且假設(shè)你已經(jīng)安裝了Python和Twisted。我所提供的示例代碼是基于Python2.5和Twisted8.2.0。 你可以在單機(jī)上運(yùn)行所有的示例代碼,也可以在網(wǎng)絡(luò)系統(tǒng)上運(yùn)行它們。但是為了學(xué)習(xí)異步編程的機(jī)制,單機(jī)上學(xué)習(xí)是比較理想的。
獲取代碼的方法

使用git工具來(lái)獲取Dave的最新示例代碼。在shell或其它命令行上輸入以下命令(假設(shè)已經(jīng)安裝git):

git clone git://github.com/jdavisp3/twisted-intro.git

下載結(jié)束后,解壓并進(jìn)入第一層文件夾(你可以看到有個(gè)README文件)。

低效的詩(shī)歌服務(wù)器
雖然CPU的處理速度遠(yuǎn)遠(yuǎn)快于網(wǎng)絡(luò),但網(wǎng)絡(luò)的處理速度仍然比人腦快,至少比人類(lèi)的眼睛快。因此,想通過(guò)網(wǎng)絡(luò)來(lái)獲得CPU的視角是很困難的,尤其是在單機(jī)的回路模式中數(shù)據(jù)流全速傳輸時(shí),更是困難重重。
我們所需要的是一個(gè)慢速低效詩(shī)歌服務(wù)器,其用人為的可變延時(shí)來(lái)體現(xiàn)對(duì)結(jié)果的影響。畢竟服務(wù)器要提供點(diǎn)東西嘛,我們就提供詩(shī)歌好了。目錄下面有個(gè)子目錄專(zhuān)門(mén)存放詩(shī)歌用的。
最簡(jiǎn)單的慢速詩(shī)歌服務(wù)器在blocking-server/slowpoetry.py中實(shí)現(xiàn)。你可用下面的方式來(lái)運(yùn)行它。

python blocking-server/slowpoetry.py poetry/ecstasy.txt

上面這個(gè)命令將啟動(dòng)一個(gè)阻塞的服務(wù)器,其提供"Ecstasy"這首詩(shī)。現(xiàn)在我們來(lái)看看它的源碼內(nèi)容,正如你所見(jiàn),這里面并沒(méi)有使用任何Twisted的內(nèi)容,只是最基本的Socket編程操作。它每次只發(fā)送一定字節(jié)數(shù)量的內(nèi)容,而每次中間延時(shí)一段時(shí)間。默認(rèn)的是每隔0.1秒發(fā)送10個(gè)比特,你可以通過(guò)--delay和 --num-bytes參數(shù)來(lái)設(shè)置。例如每隔5秒發(fā)送50比特:

python blocking-server/slowpoetry.py --num-bytes 50 –-delay 5 poetry/ecstasy.txt

當(dāng)服務(wù)器啟動(dòng)時(shí),它會(huì)顯示其所監(jiān)聽(tīng)的端口號(hào)。默認(rèn)情況下,端口號(hào)是在可用端口號(hào)池中隨機(jī)選擇的。你可能想使用固定的端口號(hào),那么無(wú)需更改代碼,只需要在啟動(dòng)命令中作下修改就OK了,如下所示:

python blocking-server/slowpoetry.py --port 10000 poetry/ecstasy.txt

如果你裝有netcat工具,可以用如下命令來(lái)測(cè)試你的服務(wù)器(也可以用telnet):

netcat localhost 10000

如果你的服務(wù)器正常工作,那么你就可以看到詩(shī)歌在你的屏幕上慢慢的打印出來(lái)。對(duì)!你會(huì)注意到每次服務(wù)器都會(huì)發(fā)送過(guò)一行的內(nèi)容過(guò)來(lái)。一旦詩(shī)歌傳送完畢,服務(wù)器就會(huì)關(guān)閉這條連接。
默認(rèn)情況下,服務(wù)器只會(huì)監(jiān)聽(tīng)本地回環(huán)的端口。如果你想連接另外一臺(tái)機(jī)子的服務(wù)器,你可以指定其IP地址內(nèi)容,命令行參數(shù)是 --iface選項(xiàng)。
不僅是服務(wù)器在發(fā)送詩(shī)歌的速度慢,而且讀代碼可以發(fā)現(xiàn),服務(wù)器在服務(wù)一個(gè)客戶端時(shí)其它連接進(jìn)來(lái)的客戶端只能處于等待狀態(tài)而得不到服務(wù)。這的確是一個(gè)低效慢速的服務(wù)器,要不是為了學(xué)習(xí),估計(jì)沒(méi)有任何其它用處。

阻塞模式的客戶端
在示例代碼中有一個(gè)可以從多個(gè)服務(wù)器中順序(一個(gè)接一個(gè))地下載詩(shī)歌的阻塞模式的客戶端。下面讓這個(gè)客戶端執(zhí)行三個(gè)任務(wù),正如第一個(gè)部分圖1描述的那樣。首先我們啟動(dòng)三個(gè)服務(wù)器,提供三首不同的詩(shī)歌。在命令行中運(yùn)行下面三條命令:

python blocking-server/slowpoetry.py --port 10000 poetry/ecstasy.txt --num-bytes 30
python blocking-server/slowpoetry.py --port 10001 poetry/fascination.txt
python blocking-server/slowpoetry.py --port 10002 poetry/science.txt

如果在你的系統(tǒng)中上面那些端口號(hào)有正在使用中,可以選擇其它沒(méi)有被使用的端口。注意,由于第一個(gè)服務(wù)器發(fā)送的詩(shī)歌是其它的三倍,這里我讓第一個(gè)服務(wù)器使用每次發(fā)送30個(gè)字節(jié)而不是默認(rèn)的10個(gè)字節(jié),這樣一來(lái)就以3倍于其它服務(wù)器的速度發(fā)送詩(shī)歌,因此它們會(huì)在幾乎相同的時(shí)間內(nèi)完成工作。
現(xiàn)在我們使用阻塞模式的客戶端來(lái)獲取詩(shī)歌,運(yùn)行如下所示的命令:
python blocking-client/get-poetry.py 10000 10001 10002
如果你修改了上面服務(wù)口器的端口,你需要在這里相應(yīng)的修改以保持一致。由于這個(gè)客戶端采用的是阻塞模式,因此它會(huì)一首一首的下載,即只有在完成一首時(shí)才會(huì)開(kāi)始下載另外一首。這個(gè)客戶端會(huì)像下面這樣打印出提示信息而不是將詩(shī)歌打印出來(lái):

Task 1: get poetry from: 127.0.0.1:10000
Task 1: got 3003 bytes of poetry from 127.0.0.1:10000 in 0:00:10.126361 
Task 2: get poetry from: 127.0.0.1:10001 
Task 2: got 623 bytes of poetry from 127.0.0.1:10001 in 0:00:06.321777
Task 3: get poetry from: 127.0.0.1:10002 
Task 3: got 653 bytes of poetry from 127.0.0.1:10002 in 0:00:06.617523
Got 3 poems in 0:00:23.065661

這圖1最典型的文字版了,每個(gè)任務(wù)下載一首詩(shī)歌。你運(yùn)行后可能顯示的時(shí)間會(huì)與上面有所差別,并且也會(huì)隨著你改變服務(wù)器的發(fā)送時(shí)間參數(shù)而改變。嘗試著更改一下參數(shù)來(lái)觀測(cè)一下效果。

異步模式的客戶端
現(xiàn)在,我們來(lái)看看不用Twisted構(gòu)建的異步模式的客戶端。首先,我們先運(yùn)行它試試。啟動(dòng)使用前面的三個(gè)端口來(lái)啟動(dòng)三個(gè)服務(wù)器。如果前面開(kāi)啟的還沒(méi)有關(guān)閉,那就繼續(xù)用它們好了。接下來(lái),我們通過(guò)下面這段命令來(lái)啟動(dòng)我們的異步模式的客戶端:

python async-client/get-poetry.py 10000 10001 10002

你或許會(huì)得到類(lèi)似于下面的輸出:

Task 1: got 30 bytes of poetry from 127.0.0.1:10000 
Task 2: got 10 bytes of poetry from 127.0.0.1:10001
Task 3: got 10 bytes of poetry from 127.0.0.1:10002
Task 1: got 30 bytes of poetry from 127.0.0.1:10000 
Task 2: got 10 bytes of poetry from 127.0.0.1:10001
...
Task 1: 3003 bytes of poetry
Task 2: 623 bytes of poetry
Task 3: 653 bytes of poetry
Got 3 poems in 0:00:10.133169

這次的輸出可能會(huì)比較長(zhǎng),這是由于在異步模式的客戶端中,每次接收到一段服務(wù)器發(fā)送來(lái)的數(shù)據(jù)都要打印一次提示信息,而服務(wù)器是將詩(shī)歌分成若干片段發(fā)送出去的。值得注意的是,這些任務(wù)相互交錯(cuò)執(zhí)行,正如第一部分圖3所示。
嘗試著修改服務(wù)器的設(shè)置(如將一個(gè)服務(wù)器的延時(shí)設(shè)置的長(zhǎng)一點(diǎn)),來(lái)觀察一下異步模式的客戶端是如何針對(duì)變慢的服務(wù)器自動(dòng)調(diào)節(jié)自身的下載來(lái)與較快的服務(wù)器保持一致。這正是異步模式在起作用。
還需要值得注意的是,根據(jù)上面的設(shè)置,異步模式的客戶端僅在10秒內(nèi)完成工作,而同步模式的客戶端卻使用了23秒?,F(xiàn)在回憶一下第一部分中圖3與圖4.通過(guò)減少阻塞時(shí)間,我們的異步模式的客戶端可以在更短的時(shí)間里完成下載。誠(chéng)然,我們的異步客戶端也有些阻塞發(fā)生,那是由于服務(wù)器太慢了。由于異步模式的客戶端可以在不同的服務(wù)器來(lái)回切換,它比同步模式的客戶產(chǎn)生的阻塞就少得多。

更近一步的觀察
現(xiàn)在讓我們來(lái)讀一下異步模式客戶端的代碼。注意其與同步模式客戶端的差別:

  • 異步模式客戶端一次性與全部服務(wù)器完成連接,而不像同步模式那樣一次只連接一個(gè)。
  • 用來(lái)進(jìn)行通信的Socket方法是非阻塞模的,這是通過(guò)調(diào)用setblocking(0)來(lái)實(shí)現(xiàn)的。
  • select模塊中的select方法是用來(lái)識(shí)別其監(jiān)視的socket是否有完成數(shù)據(jù)接收的,如果沒(méi)有它就處于阻塞狀態(tài)。
  • 當(dāng)從服務(wù)器中讀取數(shù)據(jù)時(shí),會(huì)盡量多地從Socket讀取數(shù)據(jù)直到它阻塞為止,然后讀下一個(gè)Socket接收的數(shù)據(jù)(如果有數(shù)據(jù)接收的話)。這意味著我們需要跟蹤記錄從不同服務(wù)器傳送過(guò)來(lái)詩(shī)歌的接收情況(因?yàn)?,一首?shī)的接收并不是連續(xù)完成,所以需要保證每個(gè)任務(wù)的可連續(xù)性,就得有冗余的信息來(lái)完成這一工作)。

異步模式中客戶端的核心就是最高層的循環(huán)體,即get_poetry函數(shù)。這個(gè)函數(shù)可以被拆分成兩個(gè)步驟:

  • 使用select函數(shù)等待所有Socket,直到至少有一個(gè)socket有數(shù)據(jù)到來(lái)。
  • 對(duì)每個(gè)有數(shù)據(jù)需要讀取的socket,從中讀取數(shù)據(jù)。但僅僅只是讀取有效數(shù)據(jù),不能為了等待還沒(méi)來(lái)到的數(shù)據(jù)而發(fā)生阻塞。
  • 重復(fù)前兩步,直到所有的socket被關(guān)閉。

可以看出,同步模式客戶端也有個(gè)循環(huán)體(在main函數(shù)內(nèi)),但是這個(gè)循環(huán)體的每個(gè)迭代都是完成一首詩(shī)的下載工作。而在異步模式客戶端的每次迭代過(guò)程中,我們可以完成所有詩(shī)歌的下載或者是它們中的一些。我們并不知道在一個(gè)迭代過(guò)程中,在下載哪首詩(shī),或者一次迭代中我們下載了多少數(shù)據(jù)。這些都依賴(lài)于服務(wù)器的發(fā)送速度與網(wǎng)絡(luò)環(huán)境。我們只需要select函數(shù)告訴我們哪個(gè)socket有數(shù)據(jù)需要接收,然后在保證不阻塞程序的前提下從其讀取盡量多的數(shù)據(jù)。

如果在服務(wù)器端口固定的條件下,同步模式的客戶端并不需要循環(huán)體,只需要順序羅列三個(gè)get_poetry就可以了。但是我們的異步模式的客戶端必須要有一個(gè)循環(huán)體來(lái)保證我們能夠同時(shí)監(jiān)視所有的socket端。這樣我們就能在一次循環(huán)體中處理盡可能多的數(shù)據(jù)。
這個(gè)利用循環(huán)體來(lái)等待事件發(fā)生,然后處理發(fā)生的事件的模型非常常見(jiàn),而被設(shè)計(jì)成為一個(gè)模式:reactor模式。其圖形化表示如圖5所示:

2016525103257476.png (300×366)

這個(gè)循環(huán)就是個(gè)"reactor"(反應(yīng)堆),因?yàn)樗却录陌l(fā)生然后對(duì)其作相應(yīng)的反應(yīng)。正因?yàn)槿绱?,它也被稱(chēng)作事件循環(huán)。由于交互式系統(tǒng)都要進(jìn)行I/O操作,因此這種循環(huán)也有時(shí)被稱(chēng)作select loop,這是由于select調(diào)用被用來(lái)等待I/O操作。因此,在本程序中的select循環(huán)中,一個(gè)事件的發(fā)生意味著一個(gè)socket端處有數(shù)據(jù)來(lái)到。值得注意的是,select并不是唯一的等待I/O操作的函數(shù),它僅僅是一個(gè)比較古老的函數(shù)而已(因此才被用的如此廣泛)。現(xiàn)在有一些新API可以完成select的工作而且性能更優(yōu),它們已經(jīng)在不同的系統(tǒng)上實(shí)現(xiàn)了。不考慮性能上的因素,它們都完成同樣的工作:監(jiān)視一系列sockets(文件描述符)并阻塞程序,直到至少有一個(gè)準(zhǔn)備好的I/O操作。
嚴(yán)格意義上來(lái)說(shuō),我們的異步模式客戶端中的循環(huán)并不是reactor模式,因?yàn)檫@個(gè)循環(huán)體并沒(méi)有獨(dú)立于業(yè)務(wù)處理(在此是接收具體的服務(wù)器傳送來(lái)的詩(shī)歌)之外。它們被混合在一起。一個(gè)真正reactor模式的實(shí)現(xiàn)是需要實(shí)現(xiàn)循環(huán)獨(dú)立抽象出來(lái)并具有如下的功能:

  • 監(jiān)視一系列與你I/O操作相關(guān)的文件描述符(description)
  • 不停地向你匯報(bào)那些準(zhǔn)備好的I/O操作的文件描述符

一個(gè)設(shè)計(jì)優(yōu)秀的reactor模式實(shí)現(xiàn)需要做到:

  • 處理所有不同系統(tǒng)會(huì)出現(xiàn)的I/O事件
  • 提供優(yōu)雅的抽象來(lái)幫助你在使用reactor時(shí)少花些心思去考慮它的存在
  • 提供你可以在抽象層外使用的公共協(xié)議實(shí)現(xiàn)。
  • 好了,我們上面所說(shuō)的其實(shí)就是Twisted — 健壯、跨平臺(tái)實(shí)現(xiàn)了reactor模式并含有很多附加功能。

相關(guān)文章

  • Python線程下使用鎖的技巧分享

    Python線程下使用鎖的技巧分享

    本篇文章給大家分享了Python線程下使用鎖需要注意的地方,有興趣的朋友們可以學(xué)習(xí)參考下。
    2018-09-09
  • python圖形用戶界面tkinter之按鈕Button的使用說(shuō)明

    python圖形用戶界面tkinter之按鈕Button的使用說(shuō)明

    這篇文章主要介紹了python圖形用戶界面tkinter之按鈕Button的使用說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • Pygame實(shí)戰(zhàn)練習(xí)之紙牌21點(diǎn)游戲

    Pygame實(shí)戰(zhàn)練習(xí)之紙牌21點(diǎn)游戲

    21點(diǎn)想必是很多人童年時(shí)期的經(jīng)典游戲,我們依舊能記得抱個(gè)老人機(jī)娛樂(lè)的場(chǎng)景,下面這篇文章主要給大家介紹了關(guān)于如何利用python寫(xiě)一個(gè)簡(jiǎn)單的21點(diǎn)小游戲的相關(guān)資料,需要的朋友可以參考下
    2021-09-09
  • python?中collections的?deque使用詳解

    python?中collections的?deque使用詳解

    這篇文章主要介紹了python中collections的deque使用詳解,deque是一個(gè)雙端隊(duì)列,如果要經(jīng)常從兩端append的數(shù)據(jù),選擇這個(gè)數(shù)據(jù)結(jié)構(gòu)就比較好了,更多相關(guān)內(nèi)容,需要的小伙伴可以參考下面文章內(nèi)容
    2022-09-09
  • 詳解python中的文件與目錄操作

    詳解python中的文件與目錄操作

    這篇文章主要介紹了詳解python中的文件與目錄操作的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • Python尾遞歸優(yōu)化實(shí)現(xiàn)代碼及原理詳解

    Python尾遞歸優(yōu)化實(shí)現(xiàn)代碼及原理詳解

    這篇文章主要介紹了Python尾遞歸優(yōu)化實(shí)現(xiàn)代碼及原理詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-10-10
  • PyQt5每天必學(xué)之進(jìn)度條效果

    PyQt5每天必學(xué)之進(jìn)度條效果

    這篇文章主要為大家詳細(xì)介紹了PyQt5實(shí)現(xiàn)進(jìn)度條效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-04-04
  • python抓取某汽車(chē)網(wǎng)數(shù)據(jù)解析html存入excel示例

    python抓取某汽車(chē)網(wǎng)數(shù)據(jù)解析html存入excel示例

    python抓取某汽車(chē)網(wǎng)經(jīng)銷(xiāo)商信息網(wǎng)頁(yè)數(shù)據(jù)解析html,這里提供一個(gè)示例演示,大家可以根據(jù)需要分析自己網(wǎng)站的數(shù)據(jù)
    2013-12-12
  • Python腳本修改阿里云的訪問(wèn)控制列表的方法

    Python腳本修改阿里云的訪問(wèn)控制列表的方法

    這篇文章主要介紹了Python腳本修改阿里云的訪問(wèn)控制列表的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-03-03
  • Python Django中的STATIC_URL 設(shè)置和使用方式

    Python Django中的STATIC_URL 設(shè)置和使用方式

    這篇文章主要介紹了Python Django中的STATIC_URL 設(shè)置和使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-03-03

最新評(píng)論