Периодически выполнять функцию в потоке в реальном времени, каждые N секунд

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

do_stuff()
time.sleep(0.25)

но проблема заключается в том, что не учитывается время, которое требуется для do_stuff(). Эффективно это должно быть потоком в реальном времени. Есть ли способ сделать это? В идеале поток все равно будет усыпан, когда не будет выполняться код.

Ответы

Ответ 1

Простое решение

import threading

def work (): 
  threading.Timer(0.25, work).start ()
  print "stackoverflow"

work ()

Вышеописанное будет гарантировать, что work будет выполняться с интервалом четыре раза в секунду, теория позади этого заключается в том, что он "поставит в очередь" вызов самому себе, который будет запущен на 0.25 секунды в будущее, без зависания ожидая, что это произойдет.

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


Подробнее о threading.Timer можно прочитать, следуя приведенной ниже ссылке на документацию python:


РЕКОМЕНДУЕТСЯ] Более продвинутое/динамическое решение

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

Что-то, как показано ниже, будет достаточным для этого примера, надеюсь, что код будет говорить сам за себя - он не настолько продвинут, как может показаться.

Смотрите это как вдохновение, когда вы можете реализовать свою собственную упаковку в соответствии с вашими точными потребностями.

import threading

def do_every (interval, worker_func, iterations = 0):
  if iterations != 1:
    threading.Timer (
      interval,
      do_every, [interval, worker_func, 0 if iterations == 0 else iterations-1]
    ).start ()

  worker_func ()

def print_hw ():
  print "hello world"

def print_so ():
  print "stackoverflow"


# call print_so every second, 5 times total
do_every (1, print_so, 5)

# call print_hw two times per second, forever
do_every (0.5, print_hw)

Ответ 2

Я сделал немного другой подход с одним потоком, цикл в цикле while. Для меня преимущества следующие:

  • Только один поток, другие решения, упомянутые здесь, запуск и остановка потоков для каждого интервала
  • Больше контроля для интервала, вы можете остановить IntervalTimer с помощью метода .stop()

код:

from threading import Thread, Event

# StoppableThread is from user Dolphin, from http://stackoverflow.com/info/5849484/how-to-exit-a-multithreaded-program
class StoppableThread(Thread):  

    def __init__(self):
        Thread.__init__(self)
        self.stop_event = Event()        

    def stop(self):
        if self.isAlive() == True:
            # set event to signal thread to terminate
            self.stop_event.set()
            # block calling thread until thread really has terminated
            self.join()

class IntervalTimer(StoppableThread):

    def __init__(self, interval, worker_func):
        super().__init__()
        self._interval = interval
        self._worker_func = worker_func

    def run(self):
        while not self.stop_event.is_set():
            self._worker_func()
            sleep(self._interval)

Использование:

def hw():
    print("Hello World")

interval = IntervalTimer(1,hw)
interval.start()

sleep(10)

interval.stop()