ActiveState Code

Recipe 65222: Run a task every few seconds


The TaskThread class allows you to create threads that execute a specific action at a specified interval, by subclassing - just override the task() method. You can also shutdown a TaskThread without having to wait for it to finish "sleeping" (due to the use of threading.Event objects as an alternative to time.sleep()).

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import threading


class TaskThread(threading.Thread):
    """Thread that executes a task every N seconds"""
    
    def __init__(self):
        threading.Thread.__init__(self)
        self._finished = threading.Event()
        self._interval = 15.0
    
    def setInterval(self, interval):
        """Set the number of seconds we sleep between executing our task"""
        self._interval = interval
    
    def shutdown(self):
        """Stop this thread"""
        self._finished.set()
    
    def run(self):
        while 1:
            if self._finished.isSet(): return
            self.task()
            
            # sleep for interval or until shutdown
            self._finished.wait(self._interval)
    
    def task(self):
        """The task done by this thread - override in subclasses"""
        pass

Discussion

In a lot of cases we want a thread to do something every few seconds, for example an email program may want to check if there is new mail on the POP3 server. Using TaskThread we can create classes for specific tasks - this is a great way to work with threads in general.

I know that wxPython has a wxTimer class that does the same thing, presumably using wxWindows event loop instead of threads, and there may be other ways of doing this as well.

Comments

  1. 1. At 9:17 p.m. on 18 jun 2001, Nick Perkins said:

    raise an exception to ensure subclass overrides task(). If no exception is raised by the base class task() method, it might just quietly 'pass' in the backgroud, and the error would be hard to detect.

    Instead of: def task(): pass

    using: def task(): raise Exception #( or a more specific exception )

    ..would prevent errors arising from attempting to use the TaskThread class without subclassing it, and properly overriding the task() method.

  2. 2. At 6:53 a.m. on 24 sep 2001, Ville Vainio said:

    Improvement. You might also try this:

    class PeriodicExecutor(threading.Thread):
        def __init__(self,sleep,func,params):
            """ execute func(params) every 'sleep' seconds """
            self.func = func
            self.params = params
            self.sleep = sleep
            threading.Thread.__init__(self,name = "PeriodicExecutor")
            self.setDaemon(1)
        def run(self):
            while 1:
                time.sleep(self.sleep)
                apply(self.func,self.params)
    

    It allows you to specify a function to be executed with specified params every n seconds.

  3. 3. At 9:30 a.m. on 6 jul 2002, Norbert Klamann said:

    This seems odd ... I tried to use this functionality as follows :

    if __name__ == '__main__':
        class printTaskThread(TaskThread):
            def task(self):
                print 'running'
    
        tt = printTaskThread()
        tt.setInterval(3)
        print 'starting'
        tt.run()
        print 'started, wait now'
        import time
        time.sleep(7)
        print 'killing the thread'
        tt.shutdown()
        print 'killed and done'
    

    this results in the following output under Win NT.

    E:\python.samples>python thread_its.py
    starting
    running
    running
    running
    running
    running
    running
    running
    running
    Traceback (most recent call last):
      File "thread_its.py", line 40, in ?
        tt.run()
      File "thread_its.py", line 26, in run
        self._finished.wait(self._interval)
      File "C:\python22\lib\threading.py", line 331, in wait
        self.__cond.wait(timeout)
      File "C:\python22\lib\threading.py", line 209, in wait
        _sleep(delay)
    KeyboardInterrupt
    

    So the calling program doesn't get control again. I thougt that it is the point of threads to allow just that. Wondering ...

  4. 4. At 7:30 p.m. on 1 oct 2002, Fred Petes said:

    re: This seems odd ... Change the tt.run() to tt.start()

  5. 5. At 6:52 a.m. on 17 mar 2003, Anand Balachandran Pillai said:

    Doing it in wxPython.

    A way of doing this in wxPython would be to subclass
    wxThread and post an event every interval you specify.
    Make the window / widget catch this event and let it
    process it accordingly.
    
    
    
    Something like this...
    
    
    
    MYSPECIFICEVENT=500 #or some number
    
    class MyWindow(wxFrame):
         """ Window which handles events """
    <pre>
         def __init__(self):
             ...
             EVT_MENU(self, MYSPECIFICEVENT, self.HandleEvent)
             self._scheduler=myTimer(self)
             self._scheduler.SetInterval(1000)
    
    
    
    #create the thread and run it
    self._scheduler.Create()
    self._scheduler.Run()
    
    
    
    def HandleEvent(self, event):
         """ This function handles events """
    
    
    
    id = event.GetInt()
    if id == MYSPECIFICEVENTID:
         #do something
         ....
    elif id == SOMETHINGELSE:
         #dom something else
         .....
    elif ...
         #do other things
    
    
    
    class myTimer(wxThread):
    
    
    
    def __init__(self, window):
        self._window = window # widget where work is done
        self._gap = 0
        self._timer = threading.Event()
    
    
    
    def SetInterval(self, gap):
         self._gap = gap
    
    
    
    def Entry(self):
        while 1:
            self.Sleep(self._gap)
            evt=wxCommandEvent(MYSPECIFICEVENT)
            evt.SetInt(MYSPECIFICEVENTID)
            wxPostEvent(self._window, evt)
    
    
    
    if self._timer.isSet():
         break
    
    
    
    def Exit(self):
        pass
    
    
    
    def EndThread(self):
        self._timer.set()
    
    
    
    The idea is to allow the main window or thread creator to do the work and use the thread to just post events. This has the added advantage that we can create many scheduler threads firing events with different ids and let the window perform many tasks simaltaneously.
    
    
    
    This example is courtesy the threading example in wxWindows samples.
    

    </pre>

Sign in to comment