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.
- Two threads, namely,
thread_one
andthread_two
, are spawned or created. - Next, the
start()
method of both the threads is called. Thestart()
method executes therun()
method for us. - 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 task5
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. Thejoin()
method makes sure that no thread terminates unless all the other threads are through with their tasks. - 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.