Multithreading in Python: An Easy Reference

Multithreading In Python

Multithreading in Python is a way of achieving multitasking in python using the concept of threads.

What is a Thread?

A thread is a component of any process managed by the operating system. The OS achieves parallelism or multitasking by dividing the process among threads. It is a lightweight process that ensures a separate flow of execution.

What are the Benefits of Multithreading in Python?

There are quite a few benefits to creating multithreaded applications. Let’s look at some of the benefits here:

  • Effective utilization of resources
  • More responsive
  • Resource sharing makes it more economical
  • Effective use of Multiprocessor architecture due to parallelism
  • Saves time
  • Threads (since part of the same process) communicate with each other more easily than if they were separate processes
  • They do not require much memory overhead
  • Multi-threaded servers and interactive GUIs use multithreading exclusively.

How to Achieve Multithreading in Python?

Let’s move on to creating our first multi-threaded application.

1. Import the threading module

For the creation of a thread, we will use the threading module.

import threading

The threading module consists of a Thread class which is instantiated for the creation of a thread.

A thread can be created by the creation of an object of the Thread class. The arguments for this class are as follows:

  1. target: The function to be invoked by the thread is specified here. This function is a callable object invoked by the run() method of the thread.
  2. args: Here, we specify the arguments of the target function.
def print_hi(num): 
    print("Hi, you are customer ",num)

t1 = threading.Thread(target=print_square, args=(10,))

The above code snippet invokes the print_hi() function invoked as target parameter. This function has one parameter namely num which is specified using the args.

Illustration Of Main Thread And Child Threads
Illustration Of Main Thread And Child Threads

2. Starting the Thread

The thread is started by calling the start() method of the threading module on the Thread object. The illustration of the same is shown below:

t1.start()

It must be called at most once per thread object. It arranges for the object’s run() method to be invoked in a separate thread of control.

This method will raise a RuntimeError if called more than once on the same thread object.

The thread is invoked within a program which is a process by itself. So, during the execution of the thread, the main program continues its execution as well.

Hence, we can pause the activity of the main program (run by the main thread) until the completion of the created thread. The illustration of the same is as follows.

3. Join method of Thread

def print_hi(num): 
    print("Hi, you are customer ",num)

t1 = threading.Thread(target = print_hi, args=(10,))
t1.start()
t1.join()
print("End")

In the snippet above, the Thread class is used to create an object and it has been named as t1. The start() method is invoked on the thread object t1 which marks the beginning of the thread activity.

The join() method is then called. By doing so, we ensure that the main program halts execution of the main thread and waits until the completion of thread t1. Once t1 has completed its activity, the main thread (main program) can continue its execution. Hence, the line print("End") is executed only after the completion of the thread activity.

Hi, you are customer 10
End

Without the use of the join() method, the interpreter has the choice between the two print statements – print("Hi, you are customer ", num), and print("End"). In such scenarios, it cannot be predicted which print statement will be displayed first because the execution of these lines is chosen by the interpreter.

4. Synchronizing Threads in Python

Thread synchronization is defined as a mechanism to ensure that no two threads execute a particular program segment that accesses shared resources. Such sections of the program are referred to as critical sections.

It is necessary to ensure that two threads do not access this resource simultaneously as this can lead to a race condition.

A race condition is defined as a scenario when two or more threads access the shared resources with write permission and try to change the data. Thus, the value of such variables becomes unpredictable.

Hence, we use locks in the program that temporarily halt the execution of a program until the lock is released. This is done to ensure that two threads do not access the same variable and cause conflicts.

Disadvantages of Multi-threading

  • Increases the complexity of the program.
  • Synchronization of shared resources (objects, data) is necessary.
  • Difficult to debug unpredictable results
  • Constructing and synchronizing threads is CPU/memory intensive.

References

https://docs.python.org/3/library/threading.html