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

鍍金池/ 教程/ Python/ 使用Deferred的詩歌下載客戶端
小插曲 Deferred
異步編程模式與Reactor初探
使用Deferred新功能實(shí)現(xiàn)新客戶端
由twisted支持的客戶端
增強(qiáng)defer功能的客戶端
改進(jìn)詩歌下載服務(wù)器
測(cè)試詩歌
更加"抽象"的運(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的詩歌下載客戶端
Deferreds 全貌
結(jié)束
取消之前的意圖
由Twisted扶持的客戶端
改進(jìn)詩歌下載服務(wù)器
初識(shí)Twisted

使用Deferred的詩歌下載客戶端

客戶端4.0

我們已經(jīng)對(duì)deferreds有些理解了,現(xiàn)在我們可以使用它重寫我們的客戶端。你可以在twisted-client-4/get-poetry.py中看到它的實(shí)現(xiàn)。

這里的get_poetry已經(jīng)再也不需要callback與errback參數(shù)了。相反,返回了一個(gè)用戶可能根據(jù)需要添加callbacks和errbacks的新deferred。

def get_poetry(host, port):
    """
    Download a poem from the given host and port. This function
    returns a Deferred which will be fired with the complete text of
    the poem or a Failure if the poem could not be downloaded.
    """
    d = defer.Deferred()
    from twisted.internet import reactor
    factory = PoetryClientFactory(d)
    reactor.connectTCP(host, port, factory)
    return d

這里的工廠使用一個(gè)deferred而不是callback/errback來初始化。一旦我們獲取到poem或者沒有連接到服務(wù)器,deferred就會(huì)以返回一首詩歌或一個(gè)failure的方式被激活。

class PoetryClientFactory(ClientFactory):

    protocol = PoetryProtocol

    def __init__(self, deferred):
        self.deferred = deferred

    def poem_finished(self, poem):
        if self.deferred is not None:
            d, self.deferred = self.deferred, None
            d.callback(poem)

    def clientConnectionFailed(self, connector, reason):
        if self.deferred is not None:
            d, self.deferred = self.deferred, None
            d.errback(reason)

注意我們?cè)赿eferred被激活后是如何銷毀其引用的。這種方式普便存在于Twisted的源代碼中,這樣做可以保證我們不會(huì)激活一個(gè)deferred兩次。這也為Python的垃圾回收帶來了方便。

這里仍然不用去改變poetryProtocol。我們只需要更新poetry_main函數(shù)即可:

def poetry_main():
    addresses = parse_args()

    from twisted.internet import reactor

    poems = []
    errors = []

    def got_poem(poem):
        poems.append(poem)

    def poem_failed(err):
        print >>sys.stderr, 'Poem failed:', err
        errors.append(err)

    def poem_done(_):
        if len(poems) + len(errors) == len(addresses):
            reactor.stop()

    for address in addresses:
        host, port = address
        d = get_poetry(host, port)
        d.addCallbacks(got_poem, poem_failed)
        d.addBoth(poem_done)

    reactor.run()

    for poem in poems:
        print poem

注意看我們是如何利用deferred的回調(diào)鏈特性,從先前的callback與errback回調(diào)中,重構(gòu)出poem_done調(diào)用的。

由于deferred在Twisted大量被使用,使用小寫字母d來表示當(dāng)前正在工作中的deferred已經(jīng)成為慣例。

討論

新版本的客戶端與我們前面的同步版本的客戶端一樣,get_poetry得到的參數(shù)都是詩歌下載服務(wù)器的地址。同步版本返回的是詩歌內(nèi)容,而異步版本返回的卻是一個(gè)deferred。返回一個(gè)deferred是Twisted的APIs或用Twisted寫的程序常見的,這樣一來我們可以這樣來理解deferred:

一個(gè)Deferred代表了一個(gè)"異步的結(jié)果"或者"結(jié)果還沒有到來"

在圖13中可以更加清晰地表達(dá)出兩者之間的不同:

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

異步函數(shù)返回一個(gè)deferred,對(duì)用戶意味著:

我是一個(gè)異步函數(shù),不管你想要什么,可能現(xiàn)在馬上得不到,但當(dāng)結(jié)果來到時(shí),我會(huì)激活這個(gè)deferred的callback鏈并返回結(jié)果;或者當(dāng)出錯(cuò)時(shí),相應(yīng)地激活errback鏈并返回出錯(cuò)信息。

當(dāng)然,這個(gè)函數(shù)是不能隨意激活這個(gè)deferred的,因?yàn)樗呀?jīng)返回了。但這個(gè)函數(shù)已經(jīng)啟動(dòng)了一系列事件,這些事件最終將會(huì)激活這個(gè)deferred。

因此,deferred是為適應(yīng)異步模式的一種延遲函數(shù)返回的方式。函數(shù)返回一個(gè)deferred意味著其是異步的,代表著將來的結(jié)果,也是對(duì)將來能夠返回結(jié)果的一種承諾。

同步函數(shù)也能返回一個(gè)deferred,因此嚴(yán)格來說,返回deferred只能說可能是異步的。我們會(huì)在將來的例子中會(huì)看到同步函數(shù)返回deferred。

由于deferred的行為已經(jīng)很好的定義與理解,因此在實(shí)現(xiàn)自己的API時(shí)返回一個(gè)deferred更容易讓其它的Twisted程序理解你的代碼。如果沒有deferred,可能每個(gè)人寫的模塊都使用不同的方式來處理回調(diào),如果這樣就增加了相互理解的工作量。

當(dāng)你使用Deferred時(shí),你仍然在使用回調(diào),它們?nèi)匀挥蓃eactor來調(diào)用。

當(dāng)首次學(xué)習(xí)Twisted時(shí),經(jīng)常犯的一個(gè)錯(cuò)誤就是:會(huì)給deferred增加一些它本身不能實(shí)現(xiàn)的功能。尤其是:經(jīng)常假設(shè)在deferred上添加一個(gè)函數(shù)就可以使其變成異步函數(shù)。這可能會(huì)讓你產(chǎn)生這樣的想法:在Twisted 中可以通過將os.system的函數(shù)添加到deferred的回調(diào)鏈中。

我認(rèn)為,這可能是沒有弄清楚異步編程的原因才產(chǎn)生這樣的想法。由于Twisted代碼使用了大量的deferred但卻很少會(huì)涉及到reactor,可能會(huì)認(rèn)為deferred做了大部分工作。如果你是從開始閱讀這個(gè)系列的,你就會(huì)知道事情遠(yuǎn)不是這樣。雖然Twisted是由眾多部分組合在一起來工作的,但實(shí)現(xiàn)異步的主要工作都是由reactor來完成的。Deferred是一個(gè)很好的抽象概念,但前面幾個(gè)例子中的客戶端我們卻沒有使用它,而reactor卻都用到了。

來看看我們第一個(gè)回調(diào)激活時(shí)的跟蹤棧信息。運(yùn)行twisted-client-4/get-poetry-stack.py讓其連接你打開的服務(wù)器:

  File "twisted-client-4/get-poetry-stack.py", line 129, in
    poetry_main()
  File "twisted-client-4/get-poetry-stack.py", line 122, in poetry_main
    reactor.run()

  ... # some more Twisted function calls

    protocol.connectionLost(reason)
  File "twisted-client-4/get-poetry-stack.py", line 59, in connectionLost
    self.poemReceived(self.poem)
  File "twisted-client-4/get-poetry-stack.py", line 62, in poemReceived
    self.factory.poem_finished(poem)
  File "twisted-client-4/get-poetry-stack.py", line 75, in poem_finished
    d.callback(poem) # here's where we fire the deferred

  ... # some more methods on Deferreds

  File "twisted-client-4/get-poetry-stack.py", line 105, in got_poem
    traceback.print_stack()

這很像版本2.0的跟蹤棧,圖14可以很好地說明具體的調(diào)用關(guān)系:

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

這很類似于我們前面的Twisted客戶端,雖然這張圖的調(diào)用關(guān)系并不清晰而會(huì)讓你摸不著頭腦。但我們先不深入分析這張圖。有一個(gè)細(xì)節(jié)并沒有在這張圖上反映出來:callback鏈直到第二個(gè)回調(diào)poem_done激活前才將控制權(quán)還給reactor。

通過使用deferred,我們?cè)谟蒚wisted中的reactor啟動(dòng)的回調(diào)中加入了一些自己的東西,但我們并沒有改變異步程序的基礎(chǔ)架構(gòu)。回憶下回調(diào)編程的特點(diǎn):

  1. 在一個(gè)時(shí)刻,只會(huì)有一個(gè)回調(diào)在運(yùn)行
  2. 當(dāng)reactor運(yùn)行時(shí),那我們自己的代碼則得不到運(yùn)行
  3. 反之亦然
  4. 如果我們的回調(diào)函數(shù)發(fā)生阻塞,那么整個(gè)程序就跟著阻塞掉了

在一個(gè) deferred上追加一個(gè)回調(diào)并不會(huì)改變上面這些實(shí)事。尤其是,第4 條。因此當(dāng)一個(gè)deferred激活時(shí)被阻塞,那么整個(gè)Twisted就會(huì)陷入阻塞中。因此我們會(huì)得到如下結(jié)論:

Deferred只是解決回調(diào)函數(shù)管理問題的一種解決方案,它并不替代回調(diào)方式,也不能將阻塞式的回調(diào)變成非阻塞式回調(diào)的。

我通過構(gòu)建一個(gè)添加阻塞式回調(diào)的deferred來驗(yàn)證最后一點(diǎn)。驗(yàn)證代碼文件為twisted-deferred/defer-block.py。第二個(gè)callback通過使用time.sleep來達(dá)到阻塞的效果。如果你運(yùn)行該代碼來觀察打印信息順序時(shí),你會(huì)發(fā)現(xiàn)deferred中阻塞回調(diào)仍然會(huì)是阻塞的。

總結(jié)

函數(shù)通過返回一個(gè)Deferred,向使用者暗示"我是采用異步方式的"并且當(dāng)結(jié)果到來時(shí)會(huì)使用一種特殊的機(jī)制(在此處添加你的callback與errback)來獲得返回結(jié)果。Defered被廣泛地運(yùn)用在Twisted的每個(gè)角落,當(dāng)你瀏覽Twisted源碼時(shí)你就會(huì)不停地遇到它。

4.0版本客戶端是第一個(gè)使用Deferred的Twisted版的客戶端,其使用方法為在其異步函數(shù)中返回一個(gè)deferred??梢允褂靡恍㏕wisted的APIs來使客戶端的實(shí)現(xiàn)更加清晰些,但我覺得它能夠很好地體現(xiàn)出一個(gè)簡(jiǎn)單的Twisted程序是怎么寫的了,至少對(duì)于客戶端可以如此肯定。事實(shí)上,后面我們會(huì)重構(gòu)我們的服務(wù)器端。

但我們對(duì)Deferred的講解還沒有結(jié)束。使用如此少量的代碼,Deferred就能提供如此之多的功能。我們將在第9部分探討其更多的功能和功能背后的動(dòng)機(jī)。

參考

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

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