Python Multithreading Guide

In software programming, a thread is the smallest unit of execution with the independent set of instructions. It is a part of the process and operates in the same context sharing program’s runnable resources like memory. A thread has a starting point, an execution sequence, and a result. It has an instruction pointer that holds the current state of the thread and controls what executes next in what order

Python Multithreading – Pros:

  • Multithreading can significantly improve the speed of computation on multiprocessor or multi-core systems because each processor or core handles a separate thread concurrently.
  • Multithreading allows a program to remain responsive while one thread waits for input and another runs a GUI at the same time. This statement holds true for both multiprocessor or single processor systems.
  • All the threads of a process have access to its global variables. If a global variable changes in one thread, it is visible to other threads as well. A thread can also have its own local variables.

Python Multithreading – Cons:

  • On a single processor system, multithreading wouldn’t impact the speed of computation. In fact, the system’s performance may downgrade due to the overhead of managing threads.
  • Synchronization is required to avoid mutual exclusion while accessing shared resources of the process. It directly leads to more memory and CPU utilization.
  • Multithreading increases the complexity of the program thus also making it difficult to debug.
  • It raises the possibility of potential deadlocks.
  • It may cause starvation when a thread doesn’t get regular access to shared resources. It would then fail to resume its work.

How To Use The Threading Module To Create Threads

The latest <threading> module provides rich features and greater support for threads than the legacy <thread> module discussed in the previous section. The <threading> module is an excellent example of Python Multithreading.

The <threading> module combines all the methods of the <thread> module and exposes few additional methods.

  • threading.activeCount(): It finds the total no. of active thread objects.
  • threading.currentThread(): You can use it to determine the number of thread objects in the caller’s thread control.
  • threading.enumerate(): It will give you a complete list of thread objects that are currently active.

Apart from the above methods, <threading> module also presents the <Thread> class that you can try for implementing threads. It is an object-oriented variant of Python multithreading.

The <Thread> class publishes the following methods.

Class Methods Method Description
run(): It is the entry point function for any thread.
start(): The start() method triggers a thread when run method is called.
join([time]): The join() method enables a program to wait for threads to terminate.
isAlive(): The isAlive() method verifies an active thread.
getName(): The getName() method retrieves the name of a thread.
setName(): The setName() method updates the name of a thread.

f you wish, you can refer the native Python docs to dig deeper into the <threading> module functionality.

1- Steps To Implement Threads Using The Threading Module.

You may follow the below steps to implement a new thread using the <threading> module.

  • Construct a subclass from the <Thread> class.
  • Override the <__init__(self [,args])> method to supply arguments as per requirements.
  • Next, override the <run(self [,args])> method to code the business logic of the thread.

Once you define the new <Thread> subclass, you have to instantiate it to start a new thread. Then, invoke the <start()> method to initiate it. It will eventually call the <run()> method to execute the business logic.

#Python multithreading example to print current date.
#1. Define a subclass using Thread class.
#2. Instantiate the subclass and trigger the thread. 
 
import threading
import datetime
 
class myThread (threading.Thread):
    def __init__(self, name, counter):
        threading.Thread.__init__(self)
        self.threadID = counter
        self.name = name
        self.counter = counter
    def run(self):
        print "Starting " + self.name
        print_date(self.name, self.counter)
        print "Exiting " + self.name
 
def print_date(threadName, counter):
    datefields = []
    today = datetime.date.today()
    datefields.append(today)
    print "%s[%d]: %s" % ( threadName, counter, datefields[0] )
 
# Create new threads
thread1 = myThread("Thread", 1)
thread2 = myThread("Thread", 2)
 
# Start new Threads
thread1.start()
thread2.start()
 
thread1.join()
thread2.join()
print "Exiting the Program!!!"

2- Python Multithreading – Synchronizing Threads.

The <threading> module has built in functionality to implement locking that allows you to synchronize threads. Locking is required to control access to shared resources to prevent corruption or missed data.

You can call Lock() method to apply locks, it returns the new lock object. Then, you can invoke the acquire(blocking) method of the lock object to enforce threads to run synchronously.

The optional blocking parameter specifies whether the thread waits to acquire the lock.

  • In case, blocking is set to zero, the thread returns immediately with a zero value if the lock can’t be acquired and with a 1 if the lock was acquired.
  • In case, blocking is set to 1, the thread blocks and wait for the lock to be released.

The release() method of the lock object is used to release the lock when it is no longer required.

Just for your information, Python’s built-in data structures such as lists, dictionaries are thread-safe as a side-effect of having atomic byte-codes for manipulating them. Other data structures implemented in Python or basic types like integers and floats, don’t have that protection. To guard against simultaneous access to an object, we use a Lock object.

#Python multithreading example to demonstrate locking.
#1. Define a subclass using Thread class.
#2. Instantiate the subclass and trigger the thread. 
#3. Implement locks in thread's run method. 
 
import threading
import datetime
 
exitFlag = 0
 
class myThread (threading.Thread):
    def __init__(self, name, counter):
        threading.Thread.__init__(self)
        self.threadID = counter
        self.name = name
        self.counter = counter
    def run(self):
        print "Starting " + self.name
        # Acquire lock to synchronize thread
        threadLock.acquire()
        print_date(self.name, self.counter)
        # Release lock for the next thread
        threadLock.release()
        print "Exiting " + self.name
 
def print_date(threadName, counter):
    datefields = []
    today = datetime.date.today()
    datefields.append(today)
    print "%s[%d]: %s" % ( threadName, counter, datefields[0] )
 
threadLock = threading.Lock()
threads = []
 
# Create new threads
thread1 = myThread("Thread", 1)
thread2 = myThread("Thread", 2)
 
# Start new Threads
thread1.start()
thread2.start()
 
# Add threads to thread list
threads.append(thread1)
threads.append(thread2)
 
# Wait for all threads to complete
for t in threads:
    t.join()
print "Exiting the Program!!!"

 

Leave a Reply

Your email address will not be published. Required fields are marked *