Python sched.scheduler превышает максимальную глубину рекурсии - PullRequest
5 голосов
/ 10 сентября 2009

Я недавно начал изучать Python, и часть простого приложения, которое я делаю, включает в себя таймер с дисплеем чч: мм: сс, работающим в собственном потоке.

Оглядываясь в Интернете, я нашел два способа реализации этого:

  1. Использование sched.scheduler
  2. Использование threading.Timer

То, как я это сделал, выглядит одинаково для обеих реализаций:

Плановое:

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: превышена максимальная глубина рекурсии. Я знаю, что вы можете увеличить максимальный уровень рекурсии вручную, но, конечно, здесь это не нужно?

  2. Нет ошибок, но иногда секунды пропускаются или тикают нерегулярно.

Может кто-нибудь указать мне правильное направление относительно того, как это сделать правильно? Спасибо.

Ответы [ 2 ]

5 голосов
/ 10 сентября 2009

Вот как сделать один выстрел в периодическое событие, например, с 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 может быть преобразована в другой метод, например ::10000

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, а не одна, чтобы указать «минуту спустя»; -).

4 голосов
/ 10 сентября 2009

Таймер - это одноразовое мероприятие. Это нельзя сделать таким образом.

Использование таймера для вызова функции, которая затем создает другой таймер, который вызывает функцию, которая создает таймер, который вызывает функцию, которая создает таймер, ..., должен достичь предела рекурсии.

Вы не упоминаете свою ОС, но «пропуск» или «неправильный тик» происходит по двум причинам.

  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 при создании нового события для существующего экземпляра планировщика.

...