Welcome, guest | Sign In | My Account | Store | Cart

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, 30 lines
 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

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.

5 comments

Nick Perkins 22 years, 10 months ago  # | flag

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.

Ville Vainio 22 years, 7 months ago  # | flag

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.

Norbert Klamann 21 years, 10 months ago  # | flag

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 ...

Fred Petes 21 years, 7 months ago  # | flag

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

Anand 21 years, 1 month ago  # | flag

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>