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

鍍金池/ 教程/ Python/ Deferreds 全貌
小插曲 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

Deferreds 全貌

簡介

在上一個部分,我們學(xué)習(xí)了使用生成器構(gòu)造順序異步回調(diào)的新方法, 加上 deferreds,我們現(xiàn)在有兩種將異步操作鏈接在一起的方法.

但是有時我們需要"并行"的運行一組異步操作.由于Twisted是單線程的,它實際并不會并發(fā)運行,但我們希望使用異步I/O在一組任務(wù)上盡可能快的工作.以我們的詩歌客戶端為例,它從多個服務(wù)器同時下載詩歌,而不是一個接一個的方式.這就是使用Twisted下載詩歌的全部細節(jié).

因此所有詩歌客戶端需要解決這樣一個問題:怎樣得知你啟動的所有異步操作都已經(jīng)完成?目前我們通過將結(jié)果匯總到一個列表(如客戶端 7.0中的 結(jié)果 列表)并檢查這個列表的長度來解決這個問題.除了收集成功的結(jié)果,我們還必須小心地對待失敗,否則一個失敗將使程序以為還有工作需要做而進入死循環(huán).

正如你所料,Twisted包含一個抽象層可以用來解決這個問題,我們來看一看.

DeferredList

DeferredList 類使我們可以將一個 defered 對象列表視為一個 defered 對象.通過這種方法我們啟動一族異步操作并且在它們?nèi)客瓿珊螳@得通知(無論它們成功或者失敗).讓我們看一些例子.

deferred-list/deferred-list-1.py 中,可以找到如下代碼:

from twisted.internet import defer

def got_results(res):
    print 'We got:', res

print 'Empty List.'
d = defer.DeferredList([])
print 'Adding Callback.'    
d.addCallback(got_results)

如果運行它,將得到如下輸出:

Empty List.
Adding Callback.
We got: []

注意以下幾點:

  • DeferredList 由一個Python列表初始化創(chuàng)建而成.在這種情況下,列表是空的,但我們很快將看到列表的元素必須是 Deferred 對象.
  • DeferredList 本身是一個 deferred (它繼承 Deferred).這意味著你可以像對待普通 deferred 一樣向其添加回調(diào)和錯誤回調(diào).
  • 在以上例子中,回調(diào)被添加時立即激發(fā),所以 DeferredList 也必須立即激發(fā).我們一會兒再討論.
  • deferred 列表的結(jié)果本身也是一個列表(空).

下面看一下 deferred-list/deferred-list-2.py:

from twisted.internet import defer

def got_results(res):
    print 'We got:', res

print 'One Deferred.'
d1 = defer.Deferred()
d = defer.DeferredList([d1])
print 'Adding Callback.'
d.addCallback(got_results)
print 'Firing d1.'
d1.callback('d1 result')

現(xiàn)在我們創(chuàng)建了包含一個 deferred 元素的 DeferredList 列表,得到如下輸出:

One Deferred.
Adding Callback.
Firing d1.
We got: [(True, 'd1 result')]

注意以下幾點:

  • 這次 DeferredList 沒有激發(fā)它的回調(diào),直到我們激發(fā)列表中的 deferred.
  • 結(jié)果同樣是一個列表,但這次包含一個元素.
  • 這個元素是一個元組,它的第二個值是列表中 deferred 的結(jié)果.

讓我們向列表添加兩個 deferreds (deferred-list/deferred-list-3.py):

from twisted.internet import defer

def got_results(res):
    print 'We got:', res

print 'Two Deferreds.'
d1 = defer.Deferred()
d2 = defer.Deferred()
d = defer.DeferredList([d1, d2])
print 'Adding Callback.'
d.addCallback(got_results)
print 'Firing d1.'
d1.callback('d1 result')
print 'Firing d2.'
d2.callback('d2 result')

得到如下輸出:

Two Deferreds.
Adding Callback.
Firing d1.
Firing d2.
We got: [(True, 'd1 result'), (True, 'd2 result')]

現(xiàn)在 DeferredList 的結(jié)果非常清晰,至少以我們的使用方式,它是一個列表,元素個數(shù)與傳入構(gòu)造器的 deferred 列表元素個數(shù)相同. 而且結(jié)果列表的元素包含原始的 deferreds 結(jié)果信息,至少當(dāng)這些 deferred 成功返回.這意味著 DeferredList 本身并不激發(fā)直到所有的原始列表中的 deferreds 都被激發(fā). 而且以一個空列表創(chuàng)建的 DeferredList 會立即激發(fā),因為它不需要等待任何 deferreds.

那么最終結(jié)果列表中的元素順序如何? 考慮以下代碼( deferred-list/deferred-list-4.py):

from twisted.internet import defer

def got_results(res):
    print 'We got:', res

print 'Two Deferreds.'
d1 = defer.Deferred()
d2 = defer.Deferred()
d = defer.DeferredList([d1, d2])    
print 'Adding Callback.'
d.addCallback(got_results)
print 'Firing d2.'
d2.callback('d2 result')
print 'Firing d1.'
d1.callback('d1 result')

這里我們先激發(fā) d2 然后再激發(fā) d1,注意構(gòu)造參數(shù)中的 deferred 列表里 d1, d2 仍是原先的順序.輸出結(jié)果如下:

Two Deferreds.
Adding Callback.
Firing d2.
Firing d1.
We got: [(True, 'd1 result'), (True, 'd2 result')]

輸出列表中結(jié)果的順序與原始 deferred 列表順序相對應(yīng),而不是 deferred 碰巧被激發(fā)的順序.這一點非常好,因為我們可以很容易地將每個結(jié)果與生成它的相應(yīng)的操作聯(lián)系在一起(如哪首詩來自哪個服務(wù)器).

好了,那如果列表中一個或多個 deferreds 失敗了怎么辦呢? 上面結(jié)果中的 True 有什么用? 再看一個例子(deferred-list/deferred-list-5.py):

from twisted.internet import defer

def got_results(res):
    print 'We got:', res

d1 = defer.Deferred()
d2 = defer.Deferred()
d = defer.DeferredList([d1, d2], consumeErrors=True)
d.addCallback(got_results)
print 'Firing d1.'
d1.callback('d1 result')
print 'Firing d2 with errback.'
d2.errback(Exception('d2 failure'))

現(xiàn)在我們以正常結(jié)果激發(fā) d1,以錯誤激發(fā) d2.先暫時忽略 consumerErrors 選項,稍候介紹.這里是輸出結(jié)果:

Firing d1.
Firing d2 with errback.
We got: [(True, 'd1 result'), (False, <twisted.python.failure.Failure <type 'exceptions.Exception'>>)]

這次對應(yīng) d2 的元組在第二個位置出現(xiàn)了一個 Failure,并且第一個位置是 False.至此 DeferredList 的工作原理非常清晰(但繼續(xù)瀏覽以下討論):

  • DeferredList 是以一個 deferred 對象列表創(chuàng)建的.
  • DeferredList 本身是一個 deferred,它返回的結(jié)果是一個列表,長度與 deferred 列表相同.
  • 當(dāng)原始列表中所有 deferred 被激發(fā)后, DeferredList 將會被激發(fā).
  • 結(jié)果列表中的每個元素以相同順序?qū)?yīng)原始列表中相應(yīng)的 deferred.如果某個 deferred 成功返回,相應(yīng)元素是(True,result),如果失敗則為(False,failure).
  • DeferredList 不會失敗,因為無論每個 deferred 的返回結(jié)果是什么都會被集總到結(jié)果列表中(同樣,請看下面討論).

現(xiàn)在讓我們討論一下被傳入 DeferredListconsumeErrors 選項,如果我們運行以上相同代碼而不傳入此選項(deferred-list/deferred-list-6.py),則得到以下輸出:

Firing d1.
Firing d2 with errback.
We got: [(True, 'd1 result'), (False, >twisted.python.failure.Failure >type 'exceptions.Exception'<<)]
Unhandled error in Deferred:
Traceback (most recent call last):
Failure: exceptions.Exception: d2 failure

如果你還記得,"Unhandled error in Deferred"消息是在 deferred 垃圾回收時被生成的,而且它表示最后一個回調(diào)失敗了.這個消息告訴我們并沒有完全捕獲潛在的異步錯誤.在我們例子中,它是從哪里來的呢? 很明顯不是來自 DeferredList,因為它已經(jīng)成功返回了.所以它一定是來自 d2.

DeferredList 需要知道它所監(jiān)視的 deferred 何時激發(fā). DeferredList 以通常的方式向每個 deferred 添加一個回調(diào)和錯誤回調(diào). 默認地,這個回調(diào)(或錯誤)返回原始結(jié)果(或錯誤)在將它們放入最終結(jié)果列表之后.由于錯誤回調(diào)返回原始 failure 后將觸發(fā)下一個錯誤回調(diào), d2 在它被激發(fā)后仍然保持失敗狀態(tài).

但是如果我們將 consumeErrors=True 傳遞給 DeferredList, 它將向每個 deferred 添加返回 None 的錯誤回調(diào), 即"消耗"掉這個錯誤并且取消警告信息. 我們同樣可以向 d2 添加自己的錯誤回調(diào)來處理錯誤,如 deferred-list/deferred-list-7.py.

客戶端 8.0

獲取詩歌客戶端8.0發(fā)布啦!客戶端使用 DeferredList 去發(fā)現(xiàn)所有詩歌何時完成(或失敗).新版客戶端位于 twisted-client-8/get-poetry.py. 同樣,唯一的變化在于 poetry_main, 我們來看一下重要的變化:

...
ds = []

for (host, port) in addresses:
    d = get_transformed_poem(host, port)
    d.addCallbacks(got_poem)
    ds.append(d)

dlist = defer.DeferredList(ds, consumeErrors=True)
dlist.addCallback(lambda res : reactor.stop())

你可以與 客戶端 7.0 中的相應(yīng)部分比較.

在客戶端 8.0中,我們不需要 poem_done 回調(diào)和 results 列表.相反,我們把每個從 get_transformed_poem 返回的 deferred 放入 ds 列表,之后創(chuàng)建一個 DeferredList.由于 DeferredList 不會在所有詩歌完成或失敗之前激發(fā),我們僅僅向 DeferredList 添加一個回調(diào)以便關(guān)閉 reactor. 在我們這個情況中,沒有使用 DeferredList 返回的結(jié)果,我們僅僅需要知道所有事情何時結(jié)束.僅此而已!

討論

可視化 DeferredList 的工作方式:

http://wiki.jikexueyuan.com/project/twisted-intro/images/p18_deferred-list.png" alt="" /> 圖33 DeferredList 的結(jié)果

非常簡單,真的. 還有一些關(guān)于 DeferredList 的選項我們沒有涉及,以及那些改變我們以上所描述行為的選項.我們在參考練習(xí)中把這些留給讀者自己探索.

在第十九節(jié)中我們將進一步介紹 Deferred 類, 包括 Twisted 10.1.0 提出的最新特性.

參考練習(xí)

  1. 閱讀 DeferredList 的源代碼.
  2. 修改 deferred-list 中的例子去實現(xiàn)可選的構(gòu)造器參數(shù) fireOnOneCallbackfireOnOneErrback. 實現(xiàn)你將用其中一個(或兩個都使用)的情景.
  3. 你可以使用 DeferredLists 列表創(chuàng)建一個 DeferredList 嗎? 如果是這樣,結(jié)果將是什么?
  4. 修改客戶端8.0在所有詩歌完成下載前不打印任意信息. 這次你將使用 DeferredList 的結(jié)果.
  5. 定義 DeferredDict 的句法并且實現(xiàn)它.

參考

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

本部分翻譯內(nèi)容參見luocheng @ https://github.com/luocheng/twisted-intro-cn/blob/master/p18.rst