Multithreading In Python - Introduction

Multithreading In Python - Introduction

Multi Threading Overview

Every modern operating system supports Threads. When threads were introduced in earlier days, most of the machines(not including mainframes and other mini computers) had a single processor. Popular word-processors, browsers and any other GUI based program will not prevent the user interaction when such programs had to work behind the scenes on several tasks. Example, a word-processor receiving input from user at the same time writing several blocks of data onto the harddisk. In this example, input from the user will be managed by a GUI thread and writing to the disk will be handled by a writer thread.

Multithreading on UniProcessor Systems

Remember, the CPC is just single CPU. Was the single CPU capable of executing more than one instruction or a thread at that time? No. Those CPUs were not multicore ones they were capable of executing just one instruction at a time. Then how multiple threads were executed in such single CPU systems? To continue further we need to define what is a thread. A thread is a set of instructions that forms a control flow within the same program/process. When a CPU governed by a modern operating system executes a process it does not execute all the instructions as a single stream. Instead, it executes instructions from multiple threads. When the current instruction under execution belongs to Thread A, all other threads are not in CPU. They are under the status “Under Execution”. They are in some other status. They might be in wait state, they might be in ready state and so on. Be aware of the fact that on a single CPU with single core only one thread can be executed at one point in time. The time duration in which the instruction(s) belonging to a thread are inside the CPU under execution status is called time slice. Operating systems make time slices and assign to these threads based on their priority so that these threads get more time or less time.

So it is very clear from above that the threads while executed under a Single Processor System, create an illusion to the user multiple activities within a process are executed at the same time. Yes, it is just an illusion that these threads run parallel in a Single Processor System. In reality they are executed one by one in a time sliced manner.

Multithreading on MultiProcessor Systems

With the advent of multi core systems CPUs come with multiple cores with each one of the core being capable of executing one thread at a time. When multiple threads are run in multiple cores these threads are executed in parallel at the same time. With multi core systems true parallelism is a reality, not an illusion.

Now a quick summary of multithreading: A thread is a control of execution, sequence of instructions with in a process. A process is a program that runs on the operating system.

Execution of a process moves forward as multiple threads belonging to different processes are executed inside the CPU.

Multithreading in Python

A thread in a Python program is represented by the Thread Class. In a Python program a thread can be constructed in any of the two ways below

  1. By passing a callable object inside the thread constructor.
  2. By overriding the run() method of the thread class.

1. Create a Python thread with a callable object

When creating the thread with a callable object, the function that forms the callable object, becomes the thread body. Whatever the python statements given inside that function forms a thread.

Once an object is created using a callable object the thread can be started by calling the start() method on the thread object.

e.g.,

PrimeNumberThread = Thread(target=ProducePrimeNumbers)
PrimeNumberThread.start()

In the above example ProducePrimeNumbers is a python function i.e., a callable object passed as a target parameter of the Thread constructor.

Example: A Multithread Python program to print prime numbers

from threading import Thread

#Callable function forming the thread body for the Prime Number Producer

def ProducePrimeNumbers():
    print("Prime number thread started")
    #2 is a prime number
    print(2)
    for PrimeNumberCandidate in range (2,20):
        IsPrime = False
        for divisor in range(2,PrimeNumberCandidate):
            #Skip the Composite Number
            if PrimeNumberCandidate%divisor == 0:
                IsPrime = False;
                break
            else:
            #Mark the Prime Number
                IsPrime = True;
        #Print the Prime Number
        if IsPrime == True:
            print(PrimeNumberCandidate)
    print("Prime number thread exiting")
print("Main thread started")
PrimeNumberThread = Thread(target=ProducePrimeNumbers)
#Start the prime number thread
PrimeNumberThread.start()
#Let the Main thread wait for the prime thread to complete
PrimeNumberThread.join()
print("Main thread resumed")
print("Main thread exiting")

2. Create a Python thread by overriding the run() method

from threading import Thread
# A thread class to produce Fibonacci Numbers
class FibonacciThread(Thread):
    def __init__(self):
        Thread.__init__(self)
    #override the run method to define thread body
    def run(self):
        print("Fibonacci Thread Started")
        firstTerm   = 0
        secondTerm  = 1
        nextTerm    = 0
        for i in range(0,20):
            if ( i <= 1 ):
                nextTerm = i
            else:
                nextTerm    = firstTerm + secondTerm
                firstTerm   = secondTerm
                secondTerm  = nextTerm
            print(nextTerm)
        print("Fibonacci Thread Ending")
print("Main Thread Started")
MyFibonacciThread =  FibonacciThread()
MyFibonacciThread.start()
print("Main Thread Started to wait for the Fibonacci Thread to complete");
MyFibonacciThread.join()
print("Main Thread Resumed");
print("Main Thread Ending");

Python Thread Methods (will be updated)

Written on January 12, 2020