Python Twisted:如何调度?

6 投票
2 回答
9437 浏览
提问于 2025-04-16 01:39

我在Twisted上有一天的经验,想要安排在回复TCP客户端时发送消息:

import os, sys, time
from twisted.internet import protocol, reactor

self.scenario = [(1, "Message after 1 sec!"), (4, "This after 4 secs"), (2, "End final after 2 secs")]
for timeout, data in self.scenario:
        reactor.callLater(timeout, self.sendata, data)
        print "waited %d time, sent %s\n"%(timeout, data)

现在消息可以发送了,但我遇到了两个问题:
1) “超时”是从“现在”开始计算的,我希望在每个前一个任务完成后再开始计时(也就是前一条消息发送完后再计时)
2) 我不知道在所有消息发送完后怎么关闭连接。如果我把 self.transport.loseConnection() 放在 callLater 后面,它会立刻关闭连接。

在之前的尝试中,我没有使用 reactor.callLater,而是只用 self.transport.write()time.sleep(n)for 循环中。这种情况下,所有消息是在所有超时结束后一起发送的……这并不是我想要的。
我的目的是等待客户端连接,等待超时1后发送消息1,等待超时2后发送消息2,依此类推。最后一条消息发送完后再关闭连接。

2 个回答

4

这个交易的最终解决方案..

import os, sys, time
from twisted.internet import protocol, reactor
import itertools

def sendScenario(self):
    def sendelayed(d):
        self.sendata(d)
        self.factory.out_dump.write(d)
        try:
            timeout, data = next(self.sc)
            reactor.callLater(timeout, sendelayed, data)
        except StopIteration:
            print "Scenario completed!"
            self.transport.loseConnection()

    self.scenario = [(1, "Message after 1 sec!"), (4, "This after 4 secs"), (2, "End final after 2 secs")]
    self.sc = iter(self.scenario)
    timeout, data = next(self.sc)
    reactor.callLater(timeout, sendelayed, data)
8

在使用Twisted时,有一点很重要,那就是什么都不会等待。当你调用reactor.callLater()时,你是在告诉反应器去稍后执行某个操作,而不是现在。这个调用会立刻完成(也就是在安排好这个调用后,还没执行的时候)。因此,你的print语句其实是个谎言:你并没有等待timeout的时间;你根本没有等待。

你可以用多种方法来解决这个问题,具体用哪种方法取决于你想要的效果。如果你希望第二个任务在第一个任务开始后四秒再开始,你可以简单地把第一个任务的延迟(你的timeout变量)加到第二个任务的延迟上。不过,第一个任务可能不会在你安排的时间准确开始;如果Twisted太忙,它可能会晚点开始。而且,如果你的任务耗时较长,第二个任务可能在第一个任务完成之前就开始了。

更常见的做法是让第一个任务去安排第二个任务,而不是立刻安排第二个任务。你可以在第一个任务结束后四秒再安排第二个任务(在第一个任务结束时调用reactor.callLater()),或者在第一个任务开始时四秒后再安排(在第一个任务开始时调用reactor.callLater()),或者进行更复杂的计算来决定它应该何时开始,同时记录经过的时间。

当你意识到在Twisted中什么都不会等待时,处理完成所有安排的任务后关闭连接就变得简单了:你只需让最后一个任务调用self.transport.loseConnection()。对于更复杂的情况,你可能想把Deferred连接在一起,或者使用DeferredList来在所有待处理任务完成后执行loseConnection(),即使这些任务并不是严格按顺序执行的。

撰写回答