4

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

我正在通过 TCP 发送一系列命令并等待 lineRecieved 阅读器的响应,这可能需要几秒钟的时间来处理并到达,所以我将它包装在延迟中。第一个 deferred 工作正常,但第二个触发,因为第一个仍在处理导致垃圾,因为端点一次只能处理一个命令。asysc 系统中的预期行为,但不是我需要发生的。如果我有一个或两个命令,我可以使用 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

您问题中的代码只知道如何跟踪一个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_food_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

在这种情况下,延迟链接就是这样。尽管您有 N 个 Deferred,但当您施加此顺序限制时,实际上您有一系列 2 个 Deferred。您有较早运行的 Deferred 和应仅在较早的结果后才运行的 Deferred。然后将其扩展到 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所有内容置于一致状态。这是一个微妙但重要的观点。可能存在影响协议的回调 - 例如,通过再次调用。在使该代码运行之前,协议处于一致状态很重要,否则它会混淆 - 可能是通过乱序发送请求,或者在实际发送请求时将请求排队。这是使代码安全防止重入的示例。这并不是使用 Twisted 的程序所独有的想法,但由于人们经常将重入的想法与线程程序联系起来,因此人们在编写基于 Twisted 的代码时往往会忽略这种可能性。now_d.callback(buffer)now_dgetInfo

于 2013-03-26T13:39:53.000 回答
2

基本上,如果您希望它们一个接一个地执行,您就可以彼此返回延迟。

因此,您希望 d2 仅在 d1 完成后执行,那么,从 d1 的回调中返回 d2。

换句话说,根据您的示例,您需要在 command1 回调末尾附近的某个地方调用 command2。

于 2013-03-25T23:24:55.920 回答