7. Multi-threading

Multithreading allows you to break down an application into multiple sub-tasks and run these tasks simultaneously.

If you use multithreading properly, your application speed, performance, and rendering can all be improved.

Multithreading is defined as the ability of a processor to execute multiple threads concurrently.

7.1 Process

A process is an instance of a computer program that is being executed

Its basically the program in execution. When you start an application in your computer (like a browser or text editor), the operating system creates a process.

7.2 Thread

A thread is nothing but an independent flow of execution. It can also be defined as an instance of a process.

A process can be divided into multiple independend sub-tasks, each sub-tasks is called thread.

7.3 Multi Threading in Python

Python provides one inbuilt module "threading" to provide support for developing threads. Hence developing multi threaded Programs is very easy in python.

Every Python Program by default contains one thread which is nothing but MainThread.

import threading
print("Current Executing Thread:",threading.current_thread().getName())

7.4 Ways of Creating Thread in Python

We can create a thread in Python by using 3 ways

  1. Creating a Thread without using any class
  2. Creating a Thread by extending Thread class
  3. Creating a Thread without extending Thread class
# Without multi threading:
def display():
    for i in range(1,5):
        print("Child Thread")
        time.sleep(1)

display()
for i in range(1, 5):
    print("Main Thread")
    time.sleep(.7)

7.4.1 Creating a Thread without using any class

If multiple threads present in our program, then we cannot expect execution order and hence we cannot expect exact output for the multi threaded programs.

import time
from threading import *
def display():
    for i in range(1,5):
        print("Child Thread")
        time.sleep(1)

# creating Thread object
t=Thread(target=display) 
# starting of Thread
t.start()

for i in range(1, 5):
    print("Main Thread")
    time.sleep(.7)
  • Thread with arguments
import time
from threading import *
def test(id) :
    for i in range(id):
        print(current_thread().getName(), '-->', i)
        time.sleep(0.5)

thred = [Thread(target=test , args = (i,)) for i in [5 , 2 , 3]]
print(thred)
for t in thred:
    t.start()
print(thred)

7.4.2 Creating a Thread by extending Thread class

import time
from threading import *
class MyThread(Thread):
    def run(self):
        for i in range(5):
            print("Child Thread-1")
            time.sleep(1)

t=MyThread()
t.start()
for i in range(5):
    print("Main Thread-1")
    time.sleep(0.7)

7.4.3 Creating a Thread without extending Thread class

import time
from threading import *
class Test:
    def display(self):
        for i in range(5):
            print("Child Thread-2")
            time.sleep(1)

obj=Test()
t=Thread(target=obj.display)
t.start()
for i in range(5):
    print("Main Thread-2")
    time.sleep(.7)

7.5 Shared variables using Threading

shared_var = 0 
lock_var = threading.Lock()
def test4(x) : 
    global shared_var
    with lock_var : 
        shared_var = shared_var + 1
        print("value of x %d and value of shareed_var %d " %(x, shared_var))
        time.sleep(1)
 
thread5  = [threading.Thread(target=test4 , args = (i,)) for i in [1,2,3,4,4,5]]
for t in thread5 : 
    t.start()

7.6 Terms

start()

  • When a thread instance is created, it doesn’t start executing until its start() method (which invokes the target function with the arguments you supplied) is invoked.

run()

  • The .run() method executes any target function belonging to a given thread object that is now active.
  • It normally executes in the background after the start() method is invoked.
import threading

class CustomThread(threading.Thread):
  def run(self):
    print("This is my custom run!")

custom_thread = CustomThread()
custom_thread.start()

current_thread()

  • We use the current_thread() function to get the current thread object.
import threading
print("Current Executing Thread:",threading.current_thread().getName())

Thread Identification Number (ident)

from threading import *
def test():
    print("Child Thread")

t=Thread(target=test)
t.start()
print("Main Thread Identification Number:", current_thread().ident)
print("Child Thread Identification Number:", t.ident)

active_count()

  • This function returns the number of active threads currently running
import time
from threading import *
def test(id) :
    for i in range(id):
        time.sleep(0.5)

thred = [Thread(target=test , args = (i,)) for i in [10 , 2 , 5]]
for t in thred:
    t.start()
print("The Number of active Threads:",active_count())
time.sleep(3)
print("The Number of active Threads:",active_count())

enumerate()

  • This function returns a list of all active threads currently running
from threading import *
threads =enumerate()
for i in threads:
    print(i)

isAlive()

  • isAlive() method checks whether a thread is still executing or not.

join()

  • If a thread wants to wait until completing some other thread then we should go for join() method.

Disadvantages of multithreading

  • It needs more careful synchronization.
  • My have Deadlocks and Race conditions
  • It imposes context switching overhead.
  • It can consume a large space of stocks of blocked threads.
  • It needs support for thread or process.
  • If a parent process has several threads for proper process functioning, the child processes should also be multithreaded because they may be required.

Advantages of multithreading

  • Enables efficient utilization of the resources as the threads share the data space and memory.
  • Allows the concurrent and parallel exwcution of various tasks.
  • Reduction in time consumption or response time, thereby increasing the performance.

7.7 Multi Tasking

Executing several tasks simultaneously is the concept of multitasking.

There are 2 types of Multi Tasking

  1. Process based Multi Tasking
  2. Thread based Multi Tasking

7.8 Process based Multi Tasking:

Executing several tasks simmultaneously where each task is a seperate independent process is called process based multi tasking.

Eg: while typing python program in the editor we can listen mp3 audio songs from the same system. At the same time we can download a file from the internet. All these taks are executing simultaneously and independent of each other. Hence it is process based multi tasking. This type of multi tasking is best suitable at operating system level.

7.9 Thread based MultiTasking

Executing several tasks simultaneously where each task is a seperate independent part of the same program, is called Thread based multi tasking, and each independent part is called a Thread. This type of multi tasking is best suitable at programmatic level.

Whether it is process based or thread based, the main advantage of multi tasking is to improve performance of the system by reducing response time.

Reference