I have recently started learning Python and part of the simple app I am making includes a timer with a hh:mm:ss display running in its own thread.
Looking around the web I found two ways of implementing this:
The way I did it looks similar for both implementations:
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)
Timer:
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)
Works fine with regards to ticking correctly, but generates the following error after a few minutes: RuntimeError: maximum recursion depth exceeded. I know you can increase the max recursion level manually, but surely this should not be necessary here?
No error, but occasionally the seconds will skip, or tick irregularly.
Can someone please point me in the right direction as to how to do this correctly? Thank you.
The maximum recursion depth in Python is 1000. You can change the limit by calling sys. setrecursionlimit() method.
Due to this, the recursion limit of python is usually set to a small value (approx, 10^4). This means that when you provide a large input to the recursive function, you will get an error. This is done to avoid a stack overflow. The Python interpreter limits the recursion limit so that infinite recursions are avoided.
Try increasing the recursion limit ( sys. setrecursionlimit ) or re-writing your code without recursion. Return the current value of the recursion limit, the maximum depth of the Python interpreter stack. This limit prevents infinite recursion from causing an overflow of the C stack and crashing Python.
The maximal number of nested calls (including the first one) is called recursion depth. In our case, it will be exactly n . The maximal recursion depth is limited by JavaScript engine. We can rely on it being 10000, some engines allow more, but 100000 is probably out of limit for the majority of them.
Here's how to make a one-shot into a periodic event, e.g. with sched
: if the function must make its own scheduler and be the only thing running on its thread:
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 other events must also be scheduled in the same thread then the scheduler must be made and owned "elsewhere" -- the if
part above can get refactored into another method, e.g.:
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)
Again, of course and as always with sched
, while the scheduler is running, it (and the scheduled event callbacks) will "take over" the thread in question (so you'll need to hive off a separate thread for it if you need other things to be happening at the same time).
If you need to use this kind of idiom in many functions it could be refactored into a decorator, but that would somewhat mask the underlying simplicity of the idiom, so I prefer this simple, overt use. BTW, note that time.time and time.sleep use seconds, not minutes, as their unit of time, so you need 60, not one, to indicate "a minute from now";-).
A Timer is a one-shot event. It cannot be made to loop this way.
Using a Timer to call a function which then creates another Timer which calls a function that creates a Timer which calls a function which creates a Timer, ..., must reach the recursion limit.
You don't mention your OS, but the "skipping" or "ticking irregularly" is for two reasons.
You computer is busy and "1 second" means "pretty close to 1 second, depending on what else is going on"
If you start your timer at 0.9999 seconds, and wait 1 second, you might be at 1.9999 (rounds down to 1) or 2.00000. It may appear to duplicate a time or skip a time. Your computer's internal hardware clock is very accurate, and rounding things off to the nearest second will (always) lead to the remote possibility of duplicates or skips.
Use sched correctly. http://docs.python.org/library/sched.html#module-sched
Your code snippet makes no sense for sched, either. You do not need to create a new scheduler object. You only need to create a new event.
Read http://docs.python.org/library/sched.html#sched.scheduler.enter on creating a new event for an existing scheduler instance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With