あなたの質問のコードは、1つを追跡する方法しか知りませんDeferred
。アプリケーションコードがgetInfo
、最初のアクションが結果で完了するのに十分な介入時間なしに2回呼び出すと、それ自体の内部追跡状態が破損します。
def getInfo(self,request):
print 'sending', request
self.d = defer.Deferred()
self.sendLine(request)
return self.d
d_foo = getInfo(foo)
d_bar = getInfo(bar)
このシーケンスではd_foo
、d_bar
は異なるDeferred
インスタンスです。ただし、への2回目の呼び出しgetInfo
で、属性の値はself.d
からに変更さd_foo
れd_bar
ます。d_foo
Deferred
が失われます。後で、 `lineReceivedが実行されると:
def lineReceived(self, line):
line = line.strip()
self.buffer.append(line)
if self.d is None:
return
if 'result_I_want' in self.buffer:
print 'Firing Callback'
self.d.callback(self.buffer)
self.d
その行はd_bar
おそらくfooリクエストへの応答ですが。これは、 fooのd_bar
応答を取得し、応答をまったく取得しないことを意味します。d_foo
この問題を修正するDeferred
には、プロトコル上のインスタンスのリスト(またはキュー)を保持すると役立つ場合があります。新しい情報要求が行われたときにそれに追加し、応答を受信したときにその前面からポップします。(実装しているプロトコルがわからないため、応答を構成するのに十分な行数をどのように決定するかわかりません。プロトコルでこれが定義されていない場合は、壊れているため、必要になる場合があります。より良いプロトコルに切り替えるため。)
これを修正すると、応答は少なくとも別のDeferred
インスタンスに配信されます。
また、シーケンシャル操作の強制に関する問題についても説明しました。これを解釈する方法はいくつかあります。1つの方法は、一度に1つのリクエストのみをネットワーク上で「未処理」にすることを意味するものとして解釈することです。つまり、への前の呼び出しによって返された応答データが配信されるまで、新しい要求行getInfo
を送信したくないということです。lineReceived
Deferred
getInfo
この場合、遅延連鎖はまさに問題です。N個の遅延があるという事実にもかかわらず、この順次制限を課す場合、実際には一連の2個の遅延があります。先に実行されるDeferredと、前の結果が得られた後でのみ実行する必要があるDeferredがあります。これをNに拡張するには、後のDeferredを新しいペアの前のDeferredと見なし、3番目のDeferredが新しい後のDeferredになります。
言い換えると、D1、D2、D3、およびD4がある場合は、次のようにチェーンします。
D2 is chained to D1 and only runs when D1 is complete
D3 is chained to D2 and only runs when D2 is complete
D4 is chained to D3 and only runs when D3 is complete
ただし、これは機能しますが、実際にはシリアル化を実装する最も簡単な方法ではありません。代わりに、作業を明示的にキューに入れ、:で明示的にキューgetInfo
から外すことをお勧めしlineReceived
ます。
def _sendRequest(self, d, request):
print 'sending', request
self.d = d
self.sendLine(request)
def getInfo(self,request):
if self.d is None:
d = defer.Deferred()
self._sendRequest(d, request)
return d
else:
queued_d = defer.Deferred()
self._requests.append((request, queued_d))
return queued_d
def lineReceived(self, line):
line = line.strip()
self.buffer.append(line)
if self.d is None:
return
if 'result_I_want' in self.buffer:
print 'Firing Callback'
now_d = self.d
self.d = None
buffer = self.buffer
self.buffer = []
if self._requests:
request, queued_d = self._requests.pop(0)
self._sendRequest(queued_d, request)
now_d.callback(buffer)
コード内で、行の前lineReceived
にすべてを一貫した状態にするように注意していることに注意してください。これは微妙ですが重要なポイントです。プロトコルに影響を与えるコールバックが存在する可能性があります-たとえば、再度呼び出すことによって。そのコードを実行する前に、プロトコルが一貫した状態にあることが重要です。そうしないと、混乱する可能性があります。おそらく、要求を順不同で送信したり、実際に送信する必要があるときに要求をキューに入れたりします。これは、コードを再入可能に対して安全にする例です。これはTwistedを使用するプログラムに固有のアイデアではありませんが、リエントラントのアイデアをスレッド化されたプログラムと関連付けることが最も多いため、Twistedベースのコードを作成するときにその可能性を見落とすことがよくあります。now_d.callback(buffer)
now_d
getInfo