您问题中的代码只知道如何跟踪一个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_foo
和d_bar
是不同的Deferred
实例。但是,在第二次调用 时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请求的响应。这意味着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_d
getInfo