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

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

初識Twisted

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

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

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

from twisted.internet import reactor
reactor.run()

可以用下面的命令來運行它:

python basic-twisted/simple.py

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

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

下面我們會讓這個程序豐富起來,不過事先要說幾個結(jié)論:

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

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

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

from twisted.internet import pollreactor
pollreactor.install()

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

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

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

上面這段代碼同樣沒有做任何事情。

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

你好,Twisted

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

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中找到。運行它,會得到如下結(jié)果:

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

仍然需要你手動來關(guān)掉程序,因為它在打印完畢后就又卡住了。

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

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

我們可以通過下面這段代碼來觀察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)的一些其它說明:

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

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

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

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

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

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

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

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

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

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

退出Twisted

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

下面是退出代碼,代碼文件是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!'

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

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

下面是上面程序的輸出:

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

捕獲它,Twisted

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

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

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

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

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

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

參考

本部分原作參見: dave http://krondo.com/?p=1333

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