Skip to content

Instantly share code, notes, and snippets.

@cypreess
Last active February 28, 2024 02:59
Show Gist options
  • Save cypreess/5481681 to your computer and use it in GitHub Desktop.
Save cypreess/5481681 to your computer and use it in GitHub Desktop.
Python periodic thread using timer. You can cancel this thread any time. Thread will live at maximum to the end of one single processing run, otherwise it will end in the same time (especially during a wait time for next run). This code avoids the problem of waiting very long for thread exiting, because it does not uses time.sleep(). Please be a…
import logging
import threading
class PeriodicThread(object):
"""
Python periodic Thread using Timer with instant cancellation
"""
def __init__(self, callback=None, period=1, name=None, *args, **kwargs):
self.name = name
self.args = args
self.kwargs = kwargs
self.callback = callback
self.period = period
self.stop = False
self.current_timer = None
self.schedule_lock = threading.Lock()
def start(self):
"""
Mimics Thread standard start method
"""
self.schedule_timer()
def run(self):
"""
By default run callback. Override it if you want to use inheritance
"""
if self.callback is not None:
self.callback()
def _run(self):
"""
Run desired callback and then reschedule Timer (if thread is not stopped)
"""
try:
self.run()
except Exception, e:
logging.exception("Exception in running periodic thread")
finally:
with self.schedule_lock:
if not self.stop:
self.schedule_timer()
def schedule_timer(self):
"""
Schedules next Timer run
"""
self.current_timer = threading.Timer(self.period, self._run, *self.args, **self.kwargs)
if self.name:
self.current_timer.name = self.name
self.current_timer.start()
def cancel(self):
"""
Mimics Timer standard cancel method
"""
with self.schedule_lock:
self.stop = True
if self.current_timer is not None:
self.current_timer.cancel()
def join(self):
"""
Mimics Thread standard join method
"""
self.current_timer.join()
@mayerwin
Copy link

mayerwin commented Mar 2, 2016

It seems your *self.args and **self.kwargs are not passed to callback().

@vargabalint92
Copy link

Hi cypress,

i like your script. It is simple and it does what it has to.
although i am having problem stopping it.
Could you give me some clue, ho to stop it?

Thanks

@fkilleen
Copy link

Thanks for the code snippet.
However, line 31 should be self.callback(*self.args, **self.kwargs)

@vdbpeter
Copy link

vdbpeter commented Mar 4, 2020

Thanks! Works a treat. Was struggling to achieve this functionality with the fundamental threading.Timer, then stumbled across your work.

@midiwidi
Copy link

There should also be a self.stop = False just before line 24 so that the timer can get restarted after it was stopped with cancel().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment