(回転)Twisted:第18部Deferreds全貌


概要
前のセクションでは,ジェネレータを用いて順序非同期コールバックを構築する新しい方法を学習した.  deferreds、非同期操作をリンクする方法は2つあります.
時には、しかし、私達は“並列”の1組の非同期の操作を実行する必要があります.Twistedは単一のスレッドのため、それは実際に同時に運行することはできませんが、私達は非同期I/Oを使って1組の任務の上でできるだけ速い仕事をしたいです.私達の詩のクライアントを例にして、それは複数のサーバーから同時に詩をダウンロードします.次から次へとではなく、Twistedを使って詩をダウンロードするすべての特徴です.
結果として、すべての詩クライアントは問題を解決する必要があります.どのようにして起動したすべての非同期操作が完了したことを知っていますか.クライアント7.0のような結果セットをリストにまとめることで 結果 成功した結果を収集する以外に、失敗に注意しなければならない.そうしないと、失敗はプログラムをデッドサイクルにし、仕事が必要だと思っている.
予想通り、Twistedにはこの問題を解決するための抽象層が含まれています.見てみましょう.
DeferredList DeferredList  クラスは私たちに  defered  オブジェクトリストを1つとして表示  defered  オブジェクト.この方法により、ファミリーの非同期操作を開始し、それらがすべて完了した後に通知を得る(成功または失敗にかかわらず).いくつかの例を見てみましょう.
にある 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リストから作成されます.この場合、リストは空ですが、リスト要素が  Deferred  対象.
  • DeferredList  それ自体が  deferred  (継承  Deferred).これはあなたが普通のように扱うことができることを意味します.  deferred  同様にコールバックとエラーコールバックを追加する.
  • 上記の例では、コールバックが追加されるとすぐに励起するので、  DeferredList  すぐに励起しなければなりません.後で議論します.
  • deferred  リストの結果自体もリスト(空)である.
  • 下を見て 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')

    次に、  deferred  エレメント  DeferredList  リストには、次のような出力が表示されます.
    One Deferred.
    Adding Callback.
    Firing d1.
    We got: [(True, 'd1 result')]

    次の点に注意してください.
  • 今回  DeferredList  リスト内の  deferred .
  • の結果は同じリストですが、今回は要素が含まれています.
  • この要素はメタグループで、2番目の値はリストにあります.  deferred  の結果.
  • リストに2つ追加しましょう  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')]

    現在  DeferredList  の結果は非常に明確で、少なくとも私たちの使用方法では、リストであり、要素の個数とインプットコンストラクタの  deferred  リスト要素の個数は同じで、結果リストの要素は元の  deferreds  結果情報は、少なくとも  deferred  正常に戻りました.これは  DeferredList  すべての元のリストの  deferreds  すべて励起されます.空のリストで作成されます.  DeferredList  何も待つ必要がないので、すぐに励起されます.  deferreds .
    では、最終結果リストの要素の順序はどうですか?次のコード( 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')

    ここではd 2を励起してからd 1を励起し,構造パラメータの  deferred  リスト内のd 1,d 2は元の順序である.出力結果は以下の通りである.
    Two Deferreds.
    Adding Callback.
    Firing d2.
    Firing d1.
    We got: [(True, 'd1 result'), (True, 'd2 result')]

    出力リストの結果の順序と元の  deferred  リストの順序は、  deferred  たまたま励起された順序は、各結果を生成する対応する操作(どの詩がどのサーバから来たのか)と簡単に結びつけることができるため、非常に良い.
    リストの1つ以上が  deferreds  失敗したらどうする?上の結果のTrueは何の役に立ちますか?もう1つの例(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'))

    ここで、d 1は正常な結果で励起され、d 2はエラーで励起される.  consumerErrors  オプション、説明をお待ちしています.出力結果は次のとおりです.
    Firing d1.
    Firing d2 with errback.
    We got: [(True, 'd1 result'), (False, <twisted.python.failure.Failure <type 'exceptions.Exception'>>)]

    今回d 2に対応するメタグループが2番目の位置に1つ現れた  Failure、そして最初の位置は  False.これで  DeferredList  の動作原理は非常に明確です(ただし、以下の説明を参照してください).
  • DeferredList  ひとつで  deferred  オブジェクトリスト作成.
  • DeferredList  それ自体が  deferred、結果はリストであり、長さは  deferred  リストは同じです.
  • 元のリストのすべてが  deferred  励起された後、  DeferredList  励起されます.
  • 結果リストの各要素は、元のリストの対応する  deferred.もしそれが  deferred  正常に返され、対応する要素は(True,result)、失敗した場合は(False,failure).
  • DeferredList  失敗はしない.  deferred  の戻り結果が何であるかは、結果リストに集約されます(同様に、以下を参照).
    では、転送されることについてお話ししましょう.  DeferredList  の  consumeErrors  オプションを選択します.このオプション(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  ごみ回収時に生成され、最後のコールバックに失敗したことを示します.このメッセージは、潜在的な非同期エラーを完全にキャプチャしていないことを示しています.私たちの例では、どこから来たのでしょうか.明らかに  DeferredListは、正常に戻ったので、d 2から来たに違いありません.DeferredList  監視されていることを知る必要があります  deferred  いつ励起するか.  DeferredList  通常の方法で  deferred  コールバックとエラーコールバックを追加します.デフォルトでは、このコールバック(またはエラー)は元の結果(またはエラー)を返します.最終結果リストに入れると、エラーコールバックは元の結果に戻ります.  failure  その後、次のエラーコールバックがトリガーされ、d 2は励起後も失敗状態を維持する.
    しかしconsumeErrors=Trueを  DeferredList、各  deferred  Noneに戻るエラーコールバックを追加します.つまり、このエラーを「消費」し、警告情報をキャンセルします.私たちは同様にd 2に自分のエラーコールバックを追加してエラーを処理することができます. deferred-list/deferred-list-7.py.
    クライアント8.0
    詩歌クライアント8.0のリリースを取得しました!クライアント使用  DeferredList  すべての詩がいつ完成するか(または失敗するか)を発見します.新しいクライアントは 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 の該当部分を比較する.
    クライアント8.0ではpoem_は必要ありませんdoneコールバックとresultsリストです.逆に、getからtransformed_poemが返す  deferred  dsリストを入れてから  DeferredListDeferredList  すべての詩が完成したり失敗したりする前に励まされることはありません.私たちはただ  DeferredList  コールバックを追加して閉じる  reactor.私たちの場合、使用していません.  DeferredList  戻った結果、私たちはすべてのことがいつ終わるかを知る必要があります.それだけです.
    ディスカッション
    ビジュアル化  DeferredList  の働き方:
    図37:  DeferredList  の結果
    とても簡単で、本当です.  DeferredList  私达は関连していないで、およびあれらの私达の以上述べた行为を変えるオプション.私达は参考练习の中でこれらを読者に残して自分で探求します.
    にある :doc:`p19` ではさらにご紹介します  Deferred  Twisted 10.1.0が提案した最新の特性を含むクラス.
    参考練習
  • 読書  DeferredList  のソースコード.
  • deferred-listの例を変更してオプションのコンストラクタパラメータを実現  fireOnOneCallback  および  fireOnOneErrback.そのうちの1つ(または両方)を使用するシナリオを実装します.
  • 使えます  DeferredLists  リストの作成  DeferredList  そうですか.もしそうなら、結果は何ですか?
  • 修正クライアント8.0すべての詩がダウンロードされる前に任意の情報を印刷しません.今回は  DeferredList  の結果.
  • 定義  DeferredDict  の文法を実現し、それを実現する.