4

現在、私はまだツイストの初心者であり、これは私を悩ませました。

TCP経由で一連のコマンドを送信し、lineRecievedリーダーからの応答を待っています。これは、処理して到着するまでに数秒かかる可能性があるため、遅延でラップしました。最初の遅延は正常に機能しますが、エンドポイントは一度に1つのコマンドしか処理できないため、最初の遅延はまだ処理中であるため、2番目の遅延は発生し、ガベージが発生します。asyscシステムで予想される動作ですが、必要な動作ではありません。1つまたは2つのコマンドがある場合は、deferedChainを使用して処理できますが、連続して実行するコマンドが数十ある可能性があるため、これがメンテナンス不可能なスパゲッティにすばやく変わるのではないかと心配しています。

これを行うためのクリーンな方法は何ですか?

どうもありがとう

サンプルコード

def connectionMade(self):
    self.fire_def('command1')
    print'fire command 2'
    self.fire_def('command2')#Fires when command one is running

def fire_def(self,request):
    d = self.getInfo(request)
    d.addCallback(self.print_result)
    return d

def print_result(result):
    print result


def getInfo(self,request):
    print 'sending', request
    self.d  = defer.Deferred()
    self.sendLine(request)
    return self.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'
        self.d.callback(self.buffer)
4

2 に答える 2

5

あなたの質問のコードは、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_food_barは異なるDeferredインスタンスです。ただし、への2回目の呼び出しgetInfoで、属性の値はself.dからに変更さd_food_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リクエストへの応答ですが。これは、 food_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

于 2013-03-26T13:39:53.000 に答える
2

基本的に、延期されたものを次々に実行したい場合は、延期されたものを返します。

したがって、d1が完了した後にのみd2を実行するようにします。それでは、d1のコールバックからd2を返します。

言い換えると、あなたの例では、command1のコールバックの終わり近くのどこかでcommand2を呼び出す必要があります。

于 2013-03-25T23:24:55.920 に答える