Python Multithreading Tutorial

The smallest unit of a program or process is referred to as a thread, which can operate independently or as part of a schedule determined by the Operating System. An operating system facilitates multitasking in a computer system by splitting a process into threads. A thread is a lightweight execution unit that ensures the interaction is performed independently on the system. In Python 3, when a program is executed across multiple processors, each processor operates concurrently to perform its designated tasks.

Multithreading in Python 3

Python Multithreading

Multithreading is a technique in Python programming that allows multiple threads to run concurrently by swiftly switching between them with the aid of a central processor (referred to as context switching). Furthermore, it facilitates the sharing of data space among the main threads of a process, which simplifies the exchange of information and communication compared to individual processes. The primary objective of multithreading is to execute several tasks concurrently, enhancing the application's efficiency and performance.

Note: The Python Global Interpreter Lock (GIL) allows running a single thread at a time, even the machine has multiple processors.

Benefits of Using Python for Multithreading

The following are the advantages of using Python for multithreading:

  • It guarantees powerful usage of PC framework assets.
  • Applications with multiple threads respond faster.
  • It is more cost-effective because it shares resources and its state with sub-threads (child).
  • It makes the multiprocessor engineering more viable because of closeness.
  • By running multiple threads simultaneously, it cuts down on time.
  • To store multiple threads, the system does not require a lot of memory.
  • When to use Multithreading in Python?

Utilizing multithreading is a highly effective approach for enhancing the performance and presentation of an application. Developers can execute several subtasks concurrently by employing multithreading. This allows threads to communicate with the same processor and share resources such as files, data, and memory. Furthermore, it enables users to keep a program operational, even if a segment of it is unresponsive or takes an extended period to complete.

How to achieve multithreading in Python?

In Python, there are two primary modules utilized for managing threads in a multithreading context.

  • The thread module
  • The threading module
  • Thread modules

It commenced with Python 3, which has been marked as outdated, and can exclusively be utilized with _thread that enables backward compatibility.

Syntax:

Example

thread.start_new_thread ( function_name, args[, kwargs] )

To utilize the thread module in Python, it is necessary to import the thread module first, followed by defining a function that executes a specific task by assigning the target to a variable.

Thread.py

Example

import thread # import the thread module
import time # import time module

def cal_sqre(num): # define the cal_sqre function
    print(" Calculate the square root of the given number")
    for n in num:
        time.sleep(0.3) # at each iteration it waits for 0.3 time
        print(' Square is : ', n * n)

def cal_cube(num): # define the cal_cube() function
    print(" Calculate the cube of  the given number")
    for n in num:
        time.sleep(0.3) # at each iteration it waits for 0.3 time
        print(" Cube is : ", n * n *n)

arr = [4, 5, 6, 7, 2] # given array

t1 = time.time() # get total time to execute the functions
cal_sqre(arr) # call cal_sqre() function
cal_cube(arr) # call cal_cube() function

print(" Total time taken by threads is :", time.time() - t1) # print the total time

Output:

Output

Calculate the square root of the given number
 Square is:  16
 Square is:  25
 Square is:  36
 Square is:  49
 Square is:  4
 Calculate the cube of the given number
 Cube is:  64
 Cube is:  125
 Cube is:  216
 Cube is:  343
 Cube is:  8
 Total time taken by threads is: 3.005793809890747

Threading Modules

The threading module provides a high-level approach to multithreading, facilitating the deployment of applications in Python. To utilize multithreading, it is necessary to import the threading module in a Python program.

Thread Class Methods

Methods Description
start() A start() method is used to initiate the activity of a thread. And it calls only once for each thread so that the execution of the thread can begin.
run() A run() method is used to define a thread's activity and can be overridden by a class that extends the threads class.
join() A join() method is used to block the execution of another code until the thread terminates.

Follow the given below steps to implement the threading module in Python Multithreading:

  1. Import the threading module

Initiate a new thread by utilizing the threading module, as demonstrated.

Syntax:

Example

import threading

A threading module is made up of a Thread class, which is instantiated to create a Python thread.

  1. Declaration of the thread parameters: It contains the target function, argument, and kwargs as the parameter in the Thread class.
  • Target: It defines the function name that is executed by the thread.
  • Args: It defines the arguments that are passed to the target function name.

For example:

Example

import threading
def print_hello(n):
print("Hello, how old are you ", n)
t1 = threading.Thread( target = print_hello, args =(18, ))

In the code provided, we called the print_hello function as the target argument. This function has a parameter n, which is provided to the args argument.

  1. Initiate a new thread: To create a thread using Python's multithreading, instantiate the thread class. The start method can only be invoked a single time for each thread instance; failing to adhere to this will result in an exception error.

Syntax:

Example

t1.start()
t2.start()
  1. Join function: This is a join function utilized within the thread class to pause the execution of the main thread and wait for the full completion of the thread object. Once the thread object has finished executing, the main thread resumes its execution in Python.

Joinmethod.py

Example

import threading
def print_hello(n):
	Print("Hello, how old are you? ", n)
T1 = threading.Thread( target = print_hello, args = (20, ))
T1.start()
T1.join()
Print("Thank you")

Output:

Output

Hello, how old are you? 20
Thank you

The join function halts the execution of the main thread when the aforementioned program is executed, pausing until the thread t1 has completed its operation. The main thread resumes its execution only after t1 has successfully finished.

Note: If we do not use the join method, the interpreter can execute any print statement inside the Python program. Generally, it executes the first print statement because the interpreter executes the lines of codes from the program's start.

  1. Coordinating Threads in Python

It serves as a synchronization mechanism for threads that ensures that two threads do not execute the same portion of the program concurrently when accessing shared resources. The term critical sections can be used to illustrate this scenario. To prevent the critical section condition, where two threads are prohibited from concurrently accessing resources, we utilize a race condition.

We will create a program that utilizes the threading module for multithreading in Python.

Threading.py

Example

import time # import time module
import threading
from threading import *
def cal_sqre(num): # define a square calculating function
    print(" Calculate the square root of the given number")
    for n in num: # Use for loop
        time.sleep(0.3) # at each iteration it waits for 0.3 time
        print(' Square is : ', n * n)

def cal_cube(num): # define a cube calculating function
    print(" Calculate the cube of  the given number")
    for n in num: # for loop
        time.sleep(0.3) # at each iteration it waits for 0.3 time
        print(" Cube is : ", n * n *n)

ar = [4, 5, 6, 7, 2] # given array

t = time.time() # get total time to execute the functions
#cal_cube(ar)
#cal_sqre(ar)
th1 = threading.Thread(target=cal_sqre, args=(ar, ))
th2 = threading.Thread(target=cal_cube, args=(ar, ))
th1.start()
th2.start()
th1.join()
th2.join()
print(" Total time taking by threads is :", time.time() - t) # print the total time
print(" Again executing the main thread")
print(" Thread 1 and Thread 2 have finished their execution.")

Output:

Output

Calculate the square root of the given number
 Calculate the cube of the given number
 Square is:  16
 Cube is:  64
 Square is:  25
 Cube is:  125
 Square is:  36
 Cube is:  216
 Square is:  49
 Cube is:  343
 Square is:  4
 Cube is:  8
 Total time taken by threads is: 1.5140972137451172
 Again executing the main thread
 Thread 1 and Thread 2 have finished their execution.

Input Required

This code uses input(). Please provide values below: