Multithreading in Python

Vaibhav Vaibhav Oct 10, 2023
Multithreading in Python

Multithreading refers to the technique of running multiple threads concurrently within a single process. Concurrent execution aims to utilize the full potential of computer systems and execute tasks even faster as they usually would.

Threads are remarkably lightweight and have a low memory footprint as compared to processes. Since they are very lightweight, creating a new thread is a speedy process. Running threads require fewer system resources, for example, memory, to run.

Since threads are a part of a process, they share the memory space with their parent process or the process that created them. Multithreading may seem too impressive, but not all the glitter is gold. One must be careful when working with threads because running into deadlocks and race conditions is quite ordinary. Unfortunately, due to the GIL or Global Interpreter Lock in Python, threads created by Python provide interleaving and are executed sequentially but not parallelly.

One can discover true parallelism with threads in other programming languages such as Java. If one still needs to benefit from multiple CPU cores using Python, they should consider going for multiprocessing (the technique of executing various processes parallelly).

Nevertheless, we can still perform multithreading in Python to some extent. In this article, we will learn how to perform multithreading using Python.

Multithreading in Python

We can use Python’s threading library to perform multithreading in Python. It is an in-built module that comes pre-installed with official Python, and it aims to provide thread-based parallelism in Python. To learn more about this module and what it has to offer, refer to the official documentation here.

Now, let us understand how to use this library to perform multithreading. Refer to the following Python code for the same.

import threading


class MyThread(threading.Thread):
    def __init__(self, low, high):
        super(MyThread, self).__init__()
        self.low = low
        self.high = high
        self.total = 0

    def run(self):
        for x in range(self.low, self.high):
            self.total += x

    def __str__(self):
        return f"Low: {self.low} | High: {self.high}"


thread_one = MyThread(0, 500000)
thread_two = MyThread(5000000, 10000000)
thread_one.start()
thread_two.start()
thread_one.join()
thread_two.join()
result = thread_one.total + thread_two.total
print("Result:", result)

Output:

Result: 37624997250000

The threading module provides a class Thread representing an action performed over a separate thread. An action can be anything, a mathematical computation, a POST or a GET request to an API endpoint, retrieving some output from a pre-trained machine learning model, a slice of some heavy analysis, etc.

A new class, inheriting the Thread class, has to be created, just like the MyThread class in the above Python code. Next, the Thread class has a run() method that must be overridden. This function contains the actual task or computation that the thread will conduct when activated.

As we can see, we have overridden the run() function in the above Python code. The run() function above basically loops in the range defined by the class attributes, low and high. It adds all the integers within the range to another class attribute, total.

Now that we are done with a brief description of the class let us understand how the program works. Refer to the steps ahead.

  1. Two threads, namely, thread_one and thread_two, are spawned or created.
  2. Next, the start() method of both the threads is called. The start() method executes the run() method for us.
  3. Next, the join() method is called for both the threads. This method makes sure that both the threads wait for each other before terminating. Let us say the first thread completed its task 5 seconds before the second thread. If we let the program execute further, we will run into weird bugs because the second thread is still not accomplished with its task. The join() method makes sure that no thread terminates unless all the other threads are through with their tasks.
  4. Lastly, the results of both the threads are added together, and the result is printed to the console.

Note that it is compulsory to make a summoned thread waits for other threads to complete their execution; otherwise, one can run into wrong results and errors.

To learn more about the class Thread, refer to the official documentation here.

Vaibhav Vaibhav avatar Vaibhav Vaibhav avatar

Vaibhav is an artificial intelligence and cloud computing stan. He likes to build end-to-end full-stack web and mobile applications. Besides computer science and technology, he loves playing cricket and badminton, going on bike rides, and doodling.