顺序排列扭曲的多个延迟

时间:2013-03-25 17:22:41

标签: python twisted

目前,我仍然是一个扭曲的初学者,这让我很烦恼。

我正在通过TCP发送一系列命令并等待lineRecieved阅读器的响应,这可能需要几秒钟的时间来处理和到达,因此我将其包装在deferred中。第一个延迟工作正常但第二个工作正在激活,因为第一个仍处理导致垃圾,因为端点一次只能处理一个命令。 asysc系统中的预期行为,但不是我需要发生的行为。如果我有一个或两个命令,我可以使用deferredChain来处理,但由于我可能有数十个命令按顺序运行,我担心这会变成无法维护的意大利面。

干净的方法是什么?

非常感谢

示例代码

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)

2 个答案:

答案 0 :(得分:5)

您问题中的代码只知道如何跟踪一个Deferred。如果应用程序代码调用getInfo两次没有足够的干预时间来完成第一个操作结果,那么它将破坏其自己的内部跟踪状态:

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个实例。但是,在第二次调用getInfo时,属性self.d的值已从d_foo更改为d_bard_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.dd_bar,即使该行可能是对 foo 请求的回复。这意味着d_bar将获得 foo 的响应,而d_foo将永远不会得到任何回复。

要解决此问题,可能有助于在协议上保留Deferred个实例的列表(或队列)。在发出新的信息请求时附加到它,当收到响应时从它的前面弹出。 (我不确定你正在实施什么协议,所以我不知道你将如何决定多少行足以构成一个响应。如果协议没有定义这个,那么它就会被打破,你可能想要切换到更好的协议。)

如果你解决了这个问题,那么答案至少会传递给不同的Deferred个实例。

您还描述了与强制顺序操作相关的问题。我可以通过几种方式来解释这一点。一种方法是将其解释为意味着您一次只希望一个请求在网络上“出色”。换句话说,在getInfo将响应数据发送到返回的lineReceived之前,您不希望Deferred 发送新请求行之前的致电getInfo

在这种情况下,Deferred chaining就是这样。尽管你有N个Deferreds,但当你施加这个顺序限制时,你实际上有一系列2个Deferreds。你有早期运行的延迟和延迟,只有在前一个运行结果后才能运行。您将此扩展为N,然后将后面的Deferred扩展为新对中的Deferred,并将第三个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代码如何在{/ 1}}行之前将所有内容置于一致状态中。这是一个微妙但重要的观点。 now_d.callback(buffer)上可能存在影响协议的回调 - 例如,再次调用now_d。在使代码运行之前协议处于一致状态非常重要,否则会混淆 - 可能是通过无序发送请求,或者在实际发送请求时排队请求。这是使代码安全抵御 re-entrancy 的示例。这不是Twisted-using程序所特有的想法,但由于人们通常将重新入门的想法与线程程序联系起来,人们常常忽略编写基于Twisted的代码时的可能性。

答案 1 :(得分:2)

基本上,如果你想让它们一个接一个地执行,你就会从彼此返回延迟。

所以你希望d2只在d1完成后执行,然后从d1的回调中返回d2。

换句话说,根据你的例子,你需要在command1的回调结束附近的某个地方调用command2。