在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Python/ 初識(shí)Twisted
小插曲 Deferred
異步編程模式與Reactor初探
使用Deferred新功能實(shí)現(xiàn)新客戶端
由twisted支持的客戶端
增強(qiáng)defer功能的客戶端
改進(jìn)詩(shī)歌下載服務(wù)器
測(cè)試詩(shī)歌
更加"抽象"的運(yùn)用Twisted
Deferred用于同步環(huán)境
輪子內(nèi)的輪子: Twisted和Erlang
Twisted 進(jìn)程守護(hù)
構(gòu)造"回調(diào)"的另一種方法
Twisted 理論基礎(chǔ)
惰性不是遲緩: Twisted和Haskell
第二個(gè)小插曲,deferred
使用Deferred的詩(shī)歌下載客戶端
Deferreds 全貌
結(jié)束
取消之前的意圖
由Twisted扶持的客戶端
改進(jìn)詩(shī)歌下載服務(wù)器
初識(shí)Twisted

初識(shí)Twisted

用twisted的方式實(shí)現(xiàn)前面的內(nèi)容

最終我們將使用twisted的方式來(lái)重新實(shí)現(xiàn)我們前面的異步模式客戶端。不過(guò),首先我們先稍微寫(xiě)點(diǎn)簡(jiǎn)單的twisted程序來(lái)認(rèn)識(shí)一下twisted。

最最簡(jiǎn)單的twisted程序就是下面的代碼,其在twisted-intro目錄中的basic-twisted/simple.py中。

from twisted.internet import reactor
reactor.run()

可以用下面的命令來(lái)運(yùn)行它:

python basic-twisted/simple.py

正如在第二部分所說(shuō)的那樣,twisted實(shí)現(xiàn)了Reactor模式,因此它必然會(huì)有一個(gè)對(duì)象來(lái)代表這個(gè)reactor或者說(shuō)是事件循環(huán),而這正是twisted的核心。上面代碼的第一行引入了reactor,第二行開(kāi)始啟動(dòng)事件循環(huán)。

這個(gè)程序什么事情也不做。除非你通過(guò)ctrl+c來(lái)終止它,否則它會(huì)一直運(yùn)行下去。正常情況下,我們需要給出事件循環(huán)或者文件描述符來(lái)監(jiān)視I/O(連接到某個(gè)服務(wù)器上,比如說(shuō)我們那個(gè)詩(shī)歌服務(wù)器)。后面我們會(huì)來(lái)介紹這部分內(nèi)容,現(xiàn)在這里的reactor被卡住了。值得注意的是,這里并不是一個(gè)在不停運(yùn)行的簡(jiǎn)單循環(huán)。如果你在桌面上有個(gè)CPU性能查看器,可以發(fā)現(xiàn)這個(gè)循環(huán)體不會(huì)帶來(lái)任何性能損失。實(shí)際上,這個(gè)reactor被卡住在第二部分圖5的最頂端,等待永遠(yuǎn)不會(huì)到來(lái)的事件發(fā)生(更具體點(diǎn)說(shuō)是一個(gè)調(diào)用select函數(shù),卻沒(méi)有監(jiān)視任何文件描述符)。

下面我們會(huì)讓這個(gè)程序豐富起來(lái),不過(guò)事先要說(shuō)幾個(gè)結(jié)論:

  1. Twisted的reactor只有通過(guò)調(diào)用reactor.run()才啟動(dòng)。
  2. reactor循環(huán)是在其開(kāi)始的線程中運(yùn)行,也就是運(yùn)行在主線程中。
  3. 一旦啟動(dòng),reactor就會(huì)在程序的控制下(或者具體在一個(gè)啟動(dòng)它的線程的控制下)一直運(yùn)行下去。
  4. reactor空轉(zhuǎn)時(shí)并不會(huì)消耗任何CPU的資源。
  5. 并不需要顯式的創(chuàng)建reactor,只需要引入就OK了。

最后一條需要解釋清楚。在Twisted中,reactor是Singleton模式,即在一個(gè)程序中只能有一個(gè)reactor,并且只要你引入它就相應(yīng)地創(chuàng)建一個(gè)。上面引入的方式是twisted默認(rèn)使用的方法,當(dāng)然了,twisted還有其它可以引入reactor的方法。例如,可以使用twisted.internet.pollreactor來(lái)調(diào)用poll代替select方法。

若使用其它的reactor,需要在引入twisted.internet.reactor前安裝它。下面是安裝pollreactor的方法:

from twisted.internet import pollreactor
pollreactor.install()

如果你沒(méi)有安裝其它特殊的reactor而引入了twisted.internet.reactor,那么Twisted會(huì)為你安裝selectreactor。正因?yàn)槿绱?,?xí)慣性做法不要在最頂層的模塊內(nèi)引入reactor以避免安裝默認(rèn)reactor,而是在你要使用reactor的區(qū)域內(nèi)安裝。

下面是使用 pollreactor重寫(xiě)上面的程序,可以在basic-twisted/simple-poll.py文件中找到:

from twited.internet import pollreactor
pollreactor.install()
from twisted.internet import reactor
reactor.run()

上面這段代碼同樣沒(méi)有做任何事情。

后面我們都會(huì)只使用默認(rèn)的reactor,就單純?yōu)榱藢W(xué)習(xí)來(lái)說(shuō) ,所有的reactor做的事情都一樣。

你好,Twisted

我們得用Twisted來(lái)做什么吧。下面這段代碼在reactor循環(huán)開(kāi)始后向終端打印一條消息:

def hello():
    print 'Hello from the reactor loop!'
    print 'Lately I feel like I\'m stuck in a rut.'
from twisted.internet import reactor 
reactor.callWhenRunning(hello)
print 'Starting the reactor.'
reactor.run()

這段代碼可以在basic-twisted/hello.py中找到。運(yùn)行它,會(huì)得到如下結(jié)果:

Starting the reactor. 
Hello from the reactor loop!
Lately I feel like I'm stuck in a rut.

仍然需要你手動(dòng)來(lái)關(guān)掉程序,因?yàn)樗诖蛴⊥戤吅缶陀挚ㄗ×恕?/p>

值得注意的是,hello函數(shù)是在reactor啟動(dòng)后被調(diào)用的。這意味是reactor調(diào)用的它,也就是說(shuō)Twisted在調(diào)用我們的函數(shù)。我們通過(guò)調(diào)用reactor的callWhenRunning函數(shù),并傳給它一個(gè)我們想調(diào)用函數(shù)的引用來(lái)實(shí)現(xiàn)hello函數(shù)的調(diào)用。當(dāng)然,我們必須在啟動(dòng)reactor之前完成這些工作。

我們使用回調(diào)來(lái)描述hello函數(shù)的引用?;卣{(diào)實(shí)際上就是交給Twisted(或者其它框架)的一個(gè)函數(shù)引用,這樣Twisted會(huì)在合適的時(shí)間調(diào)用這個(gè)函數(shù)引用指向的函數(shù),具體到這個(gè)程序中,是在reactor啟動(dòng)的時(shí)候調(diào)用。由于Twisted循環(huán)是獨(dú)立于我們的代碼,我們的業(yè)務(wù)代碼與reactor核心代碼的絕大多數(shù)交互都是通過(guò)使用Twisted的APIs回調(diào)我們的業(yè)務(wù)函數(shù)來(lái)實(shí)現(xiàn)的。

我們可以通過(guò)下面這段代碼來(lái)觀察Twisted是如何調(diào)用我們代碼的:

import traceback
def stack():
    print 'The python stack:'
    traceback.print_stack()
from twisted.internet import reactor
reactor.callWhenRunning(stack)
reactor.run()

這段代碼的文件是 basic-twisted/stack.py。不出意外,它的輸出是:

The python stack: 
... reactor.run() <-- This is where we called the reactor 
... ... <-- A bunch of Twisted function calls ... 
traceback.print_stack() <-- The second line in the stack function

不用考慮這其中的若干Twisted本身的函數(shù)。只需要關(guān)心reactor.run()與我們自己的函數(shù)調(diào)用之間的關(guān)系即可。

有關(guān)回調(diào)的一些其它說(shuō)明:

Twisted并不是唯一使用回調(diào)的框架。許多歷史悠久的框架都已在使用它。諸多GUI的框架也是基于回調(diào)來(lái)實(shí)現(xiàn)的,如GTK和QT。交互式程序的編程人員特別喜歡回調(diào)。也許喜歡到想嫁給它。也許已經(jīng)這樣做了。但下面這幾點(diǎn)值得我們仔細(xì)考慮下:

  1. reactor模式是單線程的。
  2. 像Twisted這種交互式模型已經(jīng)實(shí)現(xiàn)了reactor循環(huán),意味無(wú)需我們親自去實(shí)現(xiàn)它。
  3. 我們?nèi)匀恍枰蚣軄?lái)調(diào)用我們自己的代碼來(lái)完成業(yè)務(wù)邏輯。
  4. 因?yàn)樵趩尉€程中運(yùn)行,要想跑我們自己的代碼,必須在reactor循環(huán)中調(diào)用它們。
  5. reactor事先并不知道調(diào)用我們代碼的哪個(gè)函數(shù)

這樣的話,回調(diào)并不僅僅是一個(gè)可選項(xiàng),而是游戲規(guī)則的一部分。

圖6說(shuō)明了回調(diào)過(guò)程中發(fā)生的一切:

http://wiki.jikexueyuan.com/project/twisted-intro/images/p03_reactor-callback.png" alt="" />

圖6揭示了回調(diào)中的幾個(gè)重要特性:

  1. 我們的代碼與Twisted代碼運(yùn)行在同一個(gè)線程中。
  2. 當(dāng)我們的代碼運(yùn)行時(shí),Twisted代碼是處于暫停狀態(tài)的。
  3. 同樣,當(dāng)Twisted代碼處于運(yùn)行狀態(tài)時(shí),我們的代碼處于暫停狀態(tài)。
  4. reactor事件循環(huán)會(huì)在我們的回調(diào)函數(shù)返回后恢復(fù)運(yùn)行。

在一個(gè)回調(diào)函數(shù)執(zhí)行過(guò)程中,實(shí)際上Twisted的循環(huán)是被有效地阻塞在我們的代碼上的。因此,我們應(yīng)該確保回調(diào)函數(shù)不要浪費(fèi)時(shí)間(盡快返回)。特別需要強(qiáng)調(diào)的是,我們應(yīng)該盡量避免在回調(diào)函數(shù)中使用會(huì)阻塞I/O的函數(shù)。否則,我們將失去所有使用reactor所帶來(lái)的優(yōu)勢(shì)。Twisted是不會(huì)采取特殊的預(yù)防措施來(lái)防止我們使用可阻塞的代碼的,這需要我們自己來(lái)確保上面的情況不會(huì)發(fā)生。正如我們實(shí)際看到的一樣,對(duì)于普通網(wǎng)絡(luò)I/O的例子,由于我們讓Twisted替我們完成了異步通信,因此我們無(wú)需擔(dān)心上面的事情發(fā)生。

其它也可能會(huì)產(chǎn)生阻塞的操作是讀或?qū)懸粋€(gè)非socket文件描述符(如管道)或者是等待一個(gè)子進(jìn)程完成。

如何從阻塞轉(zhuǎn)換到非阻塞操作取決你具體的操作是什么,但是也有一些Twisted APIs會(huì)幫助你實(shí)現(xiàn)轉(zhuǎn)換。值得注意的是,很多標(biāo)準(zhǔn)的Python方法沒(méi)有辦法轉(zhuǎn)換為非阻塞方式。例如,os.system中的很多方法會(huì)在子進(jìn)程完成前一直處于阻塞狀態(tài),這也就是它工作的方式。所以當(dāng)你使用Twisted時(shí),避開(kāi)使用os.system。

退出Twisted

原來(lái)我們可以使用reactor的stop方法來(lái)停止Twisted的reactor。但是一旦reactor停止就無(wú)法再啟動(dòng)了。(Dave的意思是,停止就退出程序了),因此只有在你想退出程序時(shí)才執(zhí)行這個(gè)操作。

下面是退出代碼,代碼文件是basic-twisted/countdown.py

class Countdown(object):

    counter = 5

    def count(self):
        if self.counter == 0:
            reactor.stop()
        else:
            print self.counter, '...'
            self.counter -= 1
            reactor.callLater(1, self.count)

from twisted.internet import reactor

reactor.callWhenRunning(Countdown().count)

print 'Start!'
reactor.run()
print 'Stop!'

在這個(gè)程序中使用了callLater函數(shù)為T(mén)wisted注冊(cè)了一個(gè)回調(diào)函數(shù)。callLater中的第二個(gè)參數(shù)是回調(diào)函數(shù),第一個(gè)則是說(shuō)明你希望在將來(lái)幾秒鐘時(shí)執(zhí)行你的回調(diào)函數(shù)。那Twisted如何來(lái)在指定的時(shí)間執(zhí)行我們安排好的的回調(diào)函數(shù)。由于程序并沒(méi)有監(jiān)聽(tīng)任何文件描述符,為什么它沒(méi)有像前那些程序那樣卡在select循環(huán)上?select函數(shù),或者其它類(lèi)似的函數(shù),同樣會(huì)接納一個(gè)超時(shí)參數(shù)。如果在只提供一個(gè)超時(shí)參數(shù)值并且沒(méi)有可供I/O操作的文件描述符而超時(shí)時(shí)間到時(shí),select函數(shù)同樣會(huì)返回。因此,如果設(shè)置一個(gè)0的超時(shí)參數(shù),那么會(huì)無(wú)任何阻塞地立即檢查所有的文件描述符集。

你可以將超時(shí)作為圖5中循環(huán)等待中的一種事件來(lái)看待。并且Twisted使用超時(shí)事件來(lái)確保那些通過(guò)callLater函數(shù)注冊(cè)的延時(shí)回調(diào)在指定的時(shí)間執(zhí)行?;蛘吒_切的說(shuō),在指定時(shí)間的前后會(huì)執(zhí)行。如果一個(gè)回調(diào)函數(shù)執(zhí)行時(shí)間過(guò)長(zhǎng),那么下面的延時(shí)回調(diào)函數(shù)可能會(huì)被相應(yīng)的后延執(zhí)行。Twisted的callLater機(jī)制并不為硬實(shí)時(shí)系統(tǒng)提供任何時(shí)間上的保證。

下面是上面程序的輸出:

Start! 
5 ... 
4 ...
3 ... 
2 ...
1 ... 
Stop!

捕獲它,Twisted

由于Twisted經(jīng)常會(huì)在回調(diào)中結(jié)束調(diào)用我們的代碼,因此你可能會(huì)想,如果我們的回調(diào)函數(shù)中出現(xiàn)異常會(huì)發(fā)生什么狀況。(Dave的意思是說(shuō),在結(jié)束我們的回調(diào)函數(shù)后會(huì)再次回到Twisted代碼中,若在我們的回調(diào)中發(fā)生異常,那是不是異常會(huì)跑到Twisted代碼中,而造成不可想象的后果 )讓我們來(lái)試試,在basic-twisted/exception.py中的程序會(huì)在一個(gè)回調(diào)函數(shù)中引發(fā)一個(gè)異常,但是這不會(huì)影響下一個(gè)回調(diào):

def falldown():
    raise Exception('I fall down.')

def upagain():
    print 'But I get up again.'
    reactor.stop()

from twisted.internet import reactor

reactor.callWhenRunning(falldown)
reactor.callWhenRunning(upagain)

print 'Starting the reactor.'
reactor.run()

當(dāng)你在命令行中運(yùn)時(shí),會(huì)有如下的輸出:

Starting the reactor. Traceback (most recent call last):
... # I removed most of the traceback
exceptions.Exception: I fall down.
But I get up again.

注意,盡管我們看到了因第一個(gè)回調(diào)函數(shù)引發(fā)異常而出現(xiàn)的跟蹤棧,第二個(gè)回調(diào)函數(shù)依然能夠執(zhí)行。如果你將reactor.stop()注釋掉的話,程序會(huì)繼續(xù)運(yùn)行下去。所以說(shuō),reactor并不會(huì)因?yàn)榛卣{(diào)函數(shù)中出現(xiàn)失?。m然它會(huì)報(bào)告異常)而停止運(yùn)行。

網(wǎng)絡(luò)服務(wù)器通常需要這種健壯的軟件。它們通常不希望由于一個(gè)隨機(jī)的Bug導(dǎo)致崩潰。也并不是說(shuō)當(dāng)我們發(fā)現(xiàn)自己的程序內(nèi)部有問(wèn)題時(shí),就垂頭喪氣。只是想說(shuō)Twisted能夠很好的從失敗的回調(diào)中返回并繼續(xù)執(zhí)行。

繼續(xù)講解詩(shī)歌服務(wù)器

現(xiàn)在,我們已經(jīng)準(zhǔn)備好利用Twisted來(lái)搭建我們的詩(shī)歌服務(wù)器。在第4部分,我們會(huì)實(shí)現(xiàn)我們的異步模式的詩(shī)歌服務(wù)器的Twisted版。

參考

本部分原作參見(jiàn): dave http://krondo.com/?p=1333

本部分翻譯內(nèi)容參見(jiàn)楊曉偉的博客 http://blog.sina.com.cn/s/blog_704b6af70100pzhf.html