あなたの質問のコードは、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を送信したくないということです。lineReceivedDeferredgetInfo
この場合、遅延連鎖はまさに問題です。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_dgetInfo