I know this sounds a lot like this similarly-worded question, but there are differences, so bear with me.
I'm trying to create a reusable "Timer" class which calls a specified callback every N seconds, until you call stop
. As inspiration, I used the link above, with a built-in event wrapped in a stop
method. Here's how the basic class looks:
import time
import threading
from threading import Thread
from threading import Event
# Mostly inspired by https://stackoverflow.com/questions/12435211/python-threading-timer-repeat-function-every-n-seconds
class RepeatingTimer(Thread):
def __init__(self, interval_seconds, callback):
Thread.__init__(self)
self.stop_event = Event()
self.interval_seconds = interval_seconds
self.callback = callback
self.setDaemon(True)
def start(self):
while not self.stop_event.wait(self.interval_seconds):
self.callback()
time.sleep(0) # doesn't seem to do anything
def stop(self):
self.stop_event.set()
Looks good, even includes time.sleep(0)
based on this question.
It doesn't do what I thought; the call to start
never seems to return or yield, ever. Consider this use-case:
def print_status(message):
print(message)
def print_r1():
print_status("R1")
def print_r2():
print_status("R2")
r1 = RepeatingTimer(1, print_r1)
r2 = RepeatingTimer(0.5, print_r2)
r1.start()
r2.start()
The call to r1.start
never terminates. It continues on forever. The output on the console, after four seconds, is:
R1
R1
R1
R1
This prompted me to introduce the time.sleep(0)
call, although that doesn't seem to do anything.
I also tried with and without self.setDaemon(True)
, but that also seems to have no effect.
I also tried converting this into two classes: one with just the event wrappers (a StoppableTimer
class), and another that simply creates and recreates the StoppableTimer
in the callback, but that doesn't work either. Here's what it looks like:
class StoppableTimer(Thread):
def __init__(self, interval_seconds, callback):
Thread.__init__(self)
self.stop_event = Event()
self.interval_seconds = interval_seconds
self.callback = callback
self.setDaemon(True)
def start(self):
time.sleep(self.interval_seconds)
self.callback()
def stop(self):
self.stop_event.set()
class RepeatingTimer:
def __init__(self, interval_seconds, callback):
self.interval_seconds = interval_seconds
self.callback = callback
self.timer = StoppableTimer(interval_seconds, self.refresh_timer)
def start(self):
self.timer.start()
def stop(self):
self.timer.stop()
def refresh_timer(self):
self.stop()
self.callback()
self.timer = StoppableTimer(self.interval_seconds, self.refresh_timer)
self.timer.start()
I'm completely at a loss on how to make this work. I'm also mostly a beginner to Python, so please add sufficient explanation to your answer so I can grasp what the fundamental issue is.
I also read a bit about the Global Interpreter Lock on SO, but I don't understand how that could be an issue.
For reference, I'm running Python 3.6.3 on Ubuntu 17.10