In the post Python threading – an intro I introduced something about python threads, I explained the GIL (Global Interpreter Lock) and how to instantiate workers and timers.

The GIL is the topic you must know to take full advantages from python threads.

In this post I’m going to explain the following topics:

  1. Subclassing Thread
  2. Instantiating daemon thread
  3. Listing all instatiated threads
  4. Signaling threads

 

 – Subclassing Thread

Since the module “threading” is implemented in an object oriented way, every thread corresponds to an object and you can easily subclass it.
The simplest way to subclass is adding a parameter to the initialization and modify the run method to print the passed name:

''' subclassing threads '''
import threading

class SubThread(threading.Thread):
    ''' SubClass of Thread '''
    def __init__(self, name):
        threading.Thread.__init__(self)
        self.name = name 

    def run(self):
        print(self.name)

in this way the subclassed thread will print the name passed as soon as the run method is called:

if __name__ == '__main__':
    for i in range(5):
        t = SubThread('thread-'+str(i))
        t.run()

and the output will be:

$ python subclass_thread.py
thread-0
thread-1
thread-2
thread-3
thread-4

 – Instantiating daemon thread
Daemons are only useful when the main program is running, and it’s okay to kill them off once the other non-daemon threads have exited. Without daemon threads, we have to keep track of them, and tell them to exit, before our program can completely quit. By setting them as daemon threads, we can let them run and forget about them, and when our program quits, any daemon threads are killed automatically.

The following code instantiates a daemon and then two workers threads, as soon as the two worker threads terminate the daemon ends its works and the program exits:

def daemon():
    while True:
        time.sleep(1)
        print('daemon')

def worker(worker_name, timing):
    for _ in range(100):
        print(worker_name)
        time.sleep(timing)

if __name__ == '__main__':
    DAE = threading.Thread(target=daemon)
    DAE.setDaemon(True)
    DAE.start()
    TH1 = threading.Thread(target=worker, args=('TH1', 0.1,))
    TH2 = threading.Thread(target=worker, args=('TH2', 0.2,))
    TH1.start()
    TH2.start()

 – List all instantiated threads

Python threading module provides the enumerate method to retrieve a list of all Thread objects currently alive.
The list includes daemonic threads, dummy thread objects created by current_thread(), and the main thread. It excludes terminated threads and threads that have not yet been started.

For convenience I’m going to make changes to the previous piece of code:

def worker(timing):
    for _ in range(4):
        time.sleep(timing)

def list_threads():
    for thread in threading.enumerate():
        print(thread.getName())

Then the code for the main will be:

if __name__ == '__main__':
    DAE = threading.Thread(target=daemon, name='daemon')
    DAE.setDaemon(True)
    DAE.start()
    TH1 = threading.Thread(target=worker, args=(5,), name='pippo')
    TH2 = threading.Thread(target=worker, args=(5,), name='pluto')
    TH1.start()
    TH2.start()
    list_threads()

 – Signaling threads
Signaling is useful to wake up threads when a new event is received by a program.

To send signals to threads in python we need an Event object, it can be instantiated as EVT = threading.Event(). Then we can set the event. The set() method does not clear the event so we have to clear it manually. The following code sets and clears the event every 3 seconds:

def do_something():
    while True:
        print('doing_something')
        time.sleep(3)
        EVT.set()
        EVT.clear()

To receive the signal I’m going to subclass threading.Thread adding a wait_for_event method.
The init method shall accept the event object as parameter then the method wait_for_event shall call the evt.wait().

class ConsumerThread(threading.Thread):
    '''
    Implements a consumer thread subclassing threading.Thread
    '''
    def __init__(self, name, Evt):
        threading.Thread.__init__(self)
        self.name = name
        self.evt = Evt

    def run(self):
        print(self.name)
        self.main()

    def wait_for_event(self):
        '''
        @brief this method waits for an event
        @param self the class
        '''
        print(self.name + ' Waiting for event')
        event_is_set = self.evt.wait()
        print(self.name + ' event set: ', str(event_is_set))

    def main(self):
        '''
        @brief this the main function of the thread class.
        @param self the class
        '''
        while True:
            self.wait_for_event()
            print(self.name + ' dooing some job')

Now the code for the main program is as follows:

if __name__ == '__main__':
    EVT = threading.Event()
    CONSUMER = ConsumerThread('consumer thread', EVT)
    CONSUMER.start()
    TH1 = threading.Thread(target=do_something)
    TH1.start()
    print('Event is set')

And the output:

doing_something
consumer thread event set:  True
consumer thread dooing some job
consumer thread Waiting for event
doing_something
consumer thread event set:  True
consumer thread dooing some job
consumer thread Waiting for event

As usual you can find the full source code for this post at my github
at:

Gg1