Learn Python – Multithreading in Python 3- Basic and advance

A thread is the smallest unit of a software or system executed independently or scheduled through the Operating System. In the pc system, an Operating System achieves multitasking by means of dividing the process into threads. A thread is a light-weight system that ensures the execution of the system separately on the system. In Python 3, when multiple processors are running on a program, every processor runs simultaneously to execute its duties separately.

Python Multithreading

Multithreading is a threading method in Python programming to run a couple of threads at the same time as by way of unexpectedly switching between threads with a CPU assist (called context switching). Besides, it allows sharing of its information space with the fundamental threads inner a method that share data and conversation with other threads less complicated than person processes. Multithreading goals to operate multiple duties simultaneously, which increases performance, pace and improves the rendering of the application.

Note: The Python Global Interpreter Lock (GIL) approves running a single thread at a time, even the desktop has more than one processors.

Benefits of Multithreading in Python

Following are the benefits to create a multithreaded software in Python, as follows:

It ensures effective utilization of computer system resources.
Multithreaded applications are more responsive.
It shares resources and its state with sub-threads (child) which makes it more economical.
It makes the multiprocessor architecture more effective due to similarity.
It saves time by executing multiple threads at the same time.
The system does not require too much memory to store multiple threads.

When to use Multithreading in Python?

It is a very beneficial method for time-saving and improving the overall performance of an application. Multithreading permits the programmer to divide software tasks into sub-tasks and concurrently run them in a program. It lets in threads to speak and share resources such as files, data, and memory to the same processor. Furthermore, it will increase the user’s responsiveness to continue strolling a program even if a section of the software is the size or blocked.

How to achieve multithreading in Python?

There are two foremost modules of multithreading used to take care of threads in Python.

The thread module
The threading module

Thread modules

It is started out with Python 3, targeted as obsolete, and can solely be accessed with _thread that helps backward compatibility.

Syntax:

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

To enforce the thread module in Python, we need to import a thread module and then define a feature that performs some action by using putting the goal with a variable.

Thread.py

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:

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 is a high-level implementation of multithreading used to install an utility in Python. To use multithreading, we need to import the threading module in 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 beneath steps to enforce the threading module in Python Multithreading:

  1. Import the threading module

Create a new thread by way of importing the threading module, as shown.

Syntax:

import threading  

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

2 Declaration of the thread parameters: It carries the goal 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:

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

In the above code, we invoked the print_hello() function as the target parameter. The print_hello() incorporates one parameter n, which passed to the args parameter.

3 Start a new thread: To start a thread in Python multithreading, call the thread class’s object. The start() method can be referred to as once for each thread object; otherwise, it throws an exception error.

Syntax:

t1.start()  
t2.start()  

four Join method: It is a join() method used in the thread class to halt the main thread’s execution and waits until the complete execution of the thread object. When the thread object is completed, it begins the execution of the most important thread in Python.

Joinmethod.py

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:

Hello, how old are you? 20
Thank you

When the above program is executed, the join() approach halts the execution of the primary thread and waits until the thread t1 is absolutely executed. Once the t1 is effectively executed, the most important thread starts offevolved its execution.

Note: If we do now not use the join() method, the interpreter can execute any print assertion inner the Python program. Generally, it executes the first print announcement because the interpreter executes the strains of codes from the program’s start.

5. Synchronizing Threads in Python

It is a thread synchronization mechanism that ensures no two threads can concurrently execute a specific segment inner the program to get admission to the shared resources. The scenario may also be termed as vital sections. We use a race circumstance to avoid the critical section condition, in which two threads do now not get entry to sources at the equal time.

Let’s write a program to use the threading module in Python Multithreading.

Threading.py

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:

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.