ねじれた延期/コールバックと非同期実行
-
13-10-2019 - |
質問
Twistedを使用してコードをより非同期にする方法を理解しようとしています。
- 関数は繰延オブジェクトを返します
- 次に、コールバックのリストを追加します
- 最初のコールバックは、延期関数がある程度の結果を提供した後に呼び出されます
deferred_obj.callback
- 次に、コールバックのチェーンで、最初のコールバックはデータで何かを行い、2番目のコールバックを呼び出します
- や。。など。
ただし、チェーンバックは鎖でつながれており、イベントループがそれぞれが同時に発生し続けるため、非同期とは見なされません。
ただし、繰延オブジェクトがある場合、そのコールバックとしてdeferred_obj.callbackを添付した場合 d.addCallback(deferred_obj.callback)
deferred_objがデータを待っているため、これは非同期と見なされ、次にデータを渡すメソッドもデータを待っていますが、D.callback 'd'オブジェクトがデータを処理し、Deferred_objを呼び出します。ただし、このオブジェクトは延期されているため、チェーンズコールバックの場合とは異なり、非同期に実行されます...正しいですか?
私のすべてのコードが非ブロッキングであると仮定すると、これはチェーンされたコールバックが非同期ではないことを意味しますが、チェーンされた延期は正しいですか?
解決
コールバックは(デフォルトでは)同期です。ただし、 ツイストドキュメント 指摘している:
別の人を待つために延期される必要がある場合、必要なのは、追加された方法から延期されたものをAddCallbacksに返すことだけです。
したがって、それを使用して、コールバックチェーンで非同期処理を行うことができます。そうしよう:
from twisted.internet import reactor, defer
def callback_func_2(result, previous_data):
# here we pass the result of the deferred down the callback chain
# (done synchronously)
print "calling function 1 on result:%s with previous result:%s" % (result, previous_data)
return result
def callback_func(result):
#let's do some asynchronous stuff in this callback
# simple trick here is to return a deferred from a callback
# instead of the result itself.
#
# so we can do asynchronous stuff here,
# like firing something 1 second later and have
# another method processing the result
print "calling function 1 on result:%s" % result
d = defer.Deferred()
reactor.callLater(1, d.callback, "second callback")
d.addCallback(callback_func_2, result)
return d
def do():
d = defer.Deferred()
reactor.callLater(1, d.callback, "first callback")
d.addCallback(callback_func)
return d
do()
reactor.run()
他のヒント
ある種ですが、このタイプのイベント処理には並行性はありません。コードがイベントループに戻るまで、新しいコールバックは呼び出されません。したがって、コールバックのチェーンは同期しています。イベントループでは非同期です。
これは、このタイプのプログラミングの1つの注意事項です。ハンドラーは最も迅速に実行され、できるだけ早くイベントループに戻ります。ハンドラーで時間のかかるタスクを行うべきではありません。
延期されたものを使用しても、コードが非同期になりません。
import time
from twisted.internet import defer
from twisted.internet import reactor
def blocking(duration, deferred):
print "start blocking"
time.sleep(duration)
print "finished blocking"
deferred.callback(True)
def other_task():
print "working..."
reactor.callLater(1, other_task)
def finish(result):
print "stopping reactor in 2sec"
reactor.callLater(2, reactor.stop)
def failed(reason):
print reason
print "stopping reactor in 2sec"
reactor.callLater(2, reactor.stop)
def main():
d = defer.Deferred()
d.addCallbacks(finish, failed)
reactor.callLater(0, blocking, 5, d)
if __name__ == "__main__":
reactor.callLater(0, other_task)
main()
reactor.run()
長期にわたる同期コードがある場合は、Cooperator(Twisted.internet.task)を使用して、defertothreadまたは短い反復に分割することができます。
クリーンなアプローチでコードをより非同期にしたい場合は、このフレームワークをチェックしてください。
https://github.com/iogf/untwisted
明確なドキュメントを備えたきちんとしたコードがあります。非同期モデルに対処するためのアプローチは簡単です。