Python sched.scheduler超过最大递归深度

5

最近我开始学习Python,我正在制作一个简单应用程序,其中包括一个具有hh:mm:ss显示的计时器,在它自己的线程中运行。

在网上查找后,我发现了两种实现方法:

  1. 使用sched.scheduler
  2. 使用threading.Timer

我采用的方式对于两种实现看起来很相似:

sched:

def tick(self, display, alarm_time):

    # Schedule this function to run every minute
    s = sched.scheduler(time.time, time.sleep)
    s.enter(1, 1, self.tick, ([display, alarm_time]))

    # Update the time
    self.updateTime(display)

计时器:

def tick(self, display):

    # Schedule this function to run every second
    t = Timer(1, self.tick, (display,alarm_time))
    t.start()

    # Update the time
    self.updateTime(display)
  1. 在正确滴嘀嗒方面运作良好,但几分钟后会生成以下错误:RuntimeError: maximum recursion depth exceeded。我知道您可以手动增加最大递归级别,但这里肯定不需要这样做,对吧?

  2. 没有错误,但秒数偶尔会跳过或不规则地滴答。

请问有人能指导我如何正确操作吗?谢谢。


请发布您找到的使用 threading.Timer 的链接,就像这样。 - S.Lott
你的问题标题完全错误。你的计时器超过了递归深度,而不是sched.scheduler。 - S.Lott
1
建议使用计时器来自这里http://docs.python.org/library/sched.html请记住,我只是一个初学者,可能理解有误。 - user171331
2个回答

6
以下是如何将一次性事件变为定期事件的方法,例如使用 sched:如果该函数必须创建自己的调度程序,并且在其线程上是唯一运行的内容:
def tick(self, display, alarm_time, scheduler=None):
  # make a new scheduler only once & schedule this function immediately
  if scheduler is None:
    scheduler = sched.scheduler(time.time, time.sleep)
    scheduler.enter(0, 1, self.tick, ([display, alarm_time, scheduler]))
    scheduler.run()

  # reschedule this function to run again in a minute
  scheduler.enter(1, 1, self.tick, (display, alarm_time, scheduler]))

  # do whatever actual work this function requires, e.g.:
  self.updateTime(display)

如果同一线程中还必须安排其他事件,则必须在“其他地方”制作和拥有调度器--上述if部分可以重构为另一个方法,例如:

def scheduleperiodic(self, method, *args):
  self.scheduler = sched.scheduler(time.time, time.sleep)
  self.scheduler.enter(0, 1, method, args)
  # whatever else needs to be scheduled at start, if any, can go here
  self.scheduler.run()

def tick(self, display, alarm_time):
  # reschedule this function to run again in a minute
  self.scheduler.enter(60, 1, self.tick, (display, alarm_time))

  # do whatever actual work this function requires, e.g.:
  self.updateTime(display)

当然,使用 sched 时,调度器正在运行时(以及已安排的事件回调),它会“接管”所涉及的线程(因此,如果您需要同时发生其他事情,则需要将其分离为单独的线程)。

如果您需要在许多函数中使用此类习语,则可以将其重构为装饰器,但这会掩盖习语的基本简单性,因此我更喜欢这种简单、明显的用法。顺便说一下,注意 time.time 和 time.sleep 使用秒而不是分钟作为时间单位,因此您需要使用 60 而不是 1 来表示“从现在开始一分钟后”;-)。


我采纳了有关装饰器的建议。 - DrFalk3n

3
一个计时器是一次性的事件。它不能以这种方式循环。
使用计时器调用一个函数,然后创建另一个计时器调用一个创建计时器的函数,再调用一个创建计时器的函数,...,必须达到递归限制。
你没有提及你的操作系统,但“跳过”或“不规则地打勾”有两个原因。
1.你的电脑很忙,“1秒”意味着“非常接近1秒,取决于其他正在发生什么”。
2.如果你在0.9999秒开始计时,等待1秒,你可能在1.9999(四舍五入为1)或2.00000。它可能出现重复时间或跳过时间的情况。你的计算机内部硬件时钟非常精确,将事物舍入到最近的秒将(总是)导致重复或跳过的远程可能性。
正确使用sched。http://docs.python.org/library/sched.html#module-sched 你的代码片段对于sched也没有意义。你不需要创建一个新的调度程序对象。你只需要创建一个新的事件。
阅读http://docs.python.org/library/sched.html#sched.scheduler.enter关于为现有调度程序实例创建新事件的内容。

1
谢谢你的回答。我已经澄清了问题,包括我的 sched 实现和错误,但我猜测原因与你给 Timer 的原因是相同的。然而,如果sched只是在稍后安排单个事件,我仍然看不到如何避免递归限制:( - user171331
除了链接到Python文档页面之外,那么“正确的方向”是什么呢 :) - HongboZhu
调用 threading.Timer() 是递归吗?还是在调用 Timer 后函数就返回了? - HongboZhu
不,这不是正确的答案。这个回答一开始就是错的。我刚刚写了一个小脚本,什么也没做,只是使用计时器和零延迟创建了一个无限的“递归”,它已经运行了几分钟了。 - Tarnay Kálmán

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接