我发现非常奇怪的是,在捕获 t0 和 t1 内的所有 callLater 调用时,无法将时钟从 t0 提前到 t1。
根据您稍后在问题中写的内容,我将假设您指出的情况是以下示例程序演示的情况:
from twisted.internet.task import Clock
def foo(reactor, n):
if n == 0:
print "Done!"
reactor.callLater(1, foo, reactor, n - 1)
reactor = Clock()
foo(reactor, 10)
reactor.advance(10)
人们可能希望这个程序打印Done!,但事实并非如此。如果最后一行被替换为:
for i in range(10):
reactor.advance(1)
然后生成的程序会打印Done!。
Clock 以这种方式工作的原因在于它正是 真实 时钟的工作方式。据我所知,没有计算机时钟使用连续时间系统。我不会说不可能在具有离散步骤的时钟上实现一个定时事件系统,这样它就可以提供连续的时间流 - 但我会说 Twisted 没有尝试这样做。
Clock 与真正的反应器实现之间唯一真正的区别是,使用Clock,您可以使时间步长比您在典型 使用真正的反应器。
但是,对于真正的反应器来说,很可能会出现大量时间都在一个离散步骤中流逝的情况。这可能是因为系统时钟发生了变化(有一些讨论可以独立于系统时钟来安排事件,以便这种情况消失)或者可能是因为某些应用程序代码阻塞了反应器一段时间(实际上,应用程序代码总是阻塞反应堆!但在典型的程序中它只会阻塞它一段足够短的时间让大多数人忽略)。
为Clock 提供一种模拟这些大步骤的方法,可以在其中一种情况出现时为您的程序执行的操作编写测试。例如,也许你真的很在意,当内核因为 Linux I/O 电梯算法中的一个奇怪的怪癖而决定不为你的程序安排 2.1 秒时,你的物理引擎仍然计算 2.1 秒的物理,即使你的 420 次调用已跳过 200Hz 模拟循环。
可以公平地说,Twisted 提供的默认(标准?仅?)基于时间的测试工具应该对常见情况更加友好......或者不是。也许这会鼓励人们编写仅在常见情况下工作并在不常见(但最终不可避免)情况出现时在现实世界中崩溃的程序。我不确定。
关于迈克的建议,即准确地提前到下一个预定的通话,您可以轻松地做到这一点,而无需破解任何内部结构。 clock.advance(clock.getDelayedCalls()[0].getTime() - clock.seconds()) 会做到这一点(也许你会争辩 Clock 会更好,如果它至少为此提供了一个明显的辅助函数来简化对常见情况的测试)。请记住,真正的时钟不会像这样提前,所以如果您的代码在使用此技巧时在单元测试中具有某种理想的行为,请不要误以为这意味着相同的理想行为会存在于实际使用中。