Multithreading in Python | FACE Prep

Understanding Multithreading in Python requires the knowledge of multitasking. So have a quick look at the below concept of multitasking.

What is Multitasking?

In general, multitasking is a process of performing multiple tasks simultaneously. In programming, multitasking refers to the ability of an operating system to perform multiple tasks at the same time.

For example, consider you are working on MS Office, listening to songs on Youtube, downloading a movie, etc. All these tasks are performed by the same operating system simultaneously. This is what we call multitasking. Multitasking helps you to save time and at the same time, it helps increase productivity.

Multitasking is of two types namely,

  1. Process-based multitasking
  2. Thread-based multitasking

a) Process-based multitasking

Process-based multitasking is called multiprocessing. Multiprocessing allows a system to have more than two CPUs to perform multiple tasks simultaneously. Creating a process can consume more time. Multiprocessing can be classified into symmetric multiprocessing and asymmetric multiprocessing.

b) Thread-based multitasking

Thread-based multitasking is called multithreading. In this article, we will mainly focus on multithreading in Python. But, before learning about the working of multithreading, let us understand what a thread is?

What is a Thread?

Consider you are playing a game on your mobile. The game as a whole is a single process. This process has some mini processes or mini-tasks like playing music, displaying advertisements, etc. These mini-processes are referred to as threads. 

Another example – While creating a word document In MS Office, you can find some threads like spell check, grammar check, etc. 

A thread of a process has its own thread ID, program counter, registers, and stack and can execute independently.

You can check the number of threads running in your system using Task Manager –> Performance as shown in the below image.

multithreading in python

Moving on, let’s see what are the advantages of Multithreading in Python.

Why do we need Multithreading?

Similar to multitasking, multithreading is also very useful for efficient time management and performance improvement. Creating a thread is more economical when compared to creating a process. So, without any further delay, let us see how multithreading can be achieved in Python programs.

How to achieve Multithreading?

Let us understand the concept of Multithreading with an example. In the below example, we have created two classes with two methods named ‘run()’. Both methods perform the same operation of printing the given string three times. Also, ‘t1’ is the object created for the class ‘FACE’ and ‘t2’ is the object created for the class ‘Python’. 

#class 1
class FACE:
    def run(self):
        for i in range(3):
            print("FACE Prep")
#class 2
class Python:
    def run(self):
        for i in range(3):
            print("Python")

#main() method
t1 = FACE()
t2 = Python()
t1.run()
t2.run()
Output:
FACE Prep
FACE Prep
FACE Prep
Python
Python
Python

We need both the methods to execute simultaneously. But, the ‘run()’ method of the class ‘FACE’ is executed first followed by the ‘run()’ method of the class ‘Python’. This is because, by default, every execution of a program has one thread called the main thread. Even if you are not creating your own thread explicitly, Python provides this main thread. So, the execution of the above code is because of the main thread. 

‘Thread’ class

To execute both the methods in parallel, we need to change two classes as two different threads. We can change a class as a thread by making a class as a subclass of Thread class, i.e., the classes ‘FACE’ and ‘Python’ need to extend the class ‘Thread’. To use the ‘Thread’ class in our program, we need to import a module named ‘threading’ as shown in the below example.

from threading import *
#thread 1
class FACE(Thread):
    def run(self):
        for i in range(3):
            print("FACE Prep")
#thread2
class Python(Thread):
    def run(self):
        for i in range(3):
            print("Python")

#main() method
t1 = FACE()
t2 = Python()
t1.run()
t2.run()
Output:
FACE Prep
FACE Prep
FACE Prep
Python
Python
Python

But still, we are getting outputs one after the other. Now that we have changed two classes into two different threads. So instead of calling the threads using the method name ‘run’, we need to call using the name ‘start’. To put simple, ‘start’ is used to start the execution of a thread.

start()

When we call the methods of the threads using the name ‘start’, internally the Python interpreter will search for the ‘run’ method. Hence, we have named the methods as ‘run’. If we name the methods with some other names, our code will not work. We can clearly observe the parallel working of two threads by making individual threads to sleep for some time. 

In the below example, we have made the threads to sleep for one second. Once the first thread prints the string “FACE Prep”, it will go to sleep state for one second. Meanwhile, the second thread will print the string “Python” and goes to the sleep state. This process continues for three times.

from time import sleep
from threading import *
#thread1
class FACE(Thread):
    def run(self):
        for i in range(3):
            print("FACE Prep")
            sleep(1)
#thread2
class Python(Thread):
    def run(self):
        for i in range(3):
            print("Python")
            sleep(1)

#main() method
t1 = FACE()
t2 = Python()
t1.start()  #starting thread 1
t2.start()  #starting thread 2
Output:
FACE Prep
Python
FACE Prep
Python
FACE Prep
Python

From the output, you can see that the two threads worked simultaneously.

join()

Now, let us add one more print statement at the end of the main() method as shown below.

from time import sleep
from threading import *
#thread1
class FACE(Thread):
    def run(self):
        for i in range(3):
            print("FACE Prep")
            sleep(1)
#thread2
class Python(Thread):
    def run(self):
        for i in range(3):
            print("Python")
            sleep(1)

#main() method
t1 = FACE()
t2 = Python()
t1.start()
t2.start()
print("Bye")
Output:
FACE Prep
Python
Bye
FACE Prep
Python
FACE Prep
Python

Here, the string “Bye” gets printed even before the execution of the two threads gets complete. This is because of the main thread. But, this should not happen. We need “Bye” to be printed at the end. So, we need to make the main thread wait until two threads complete their execution. This can be done by using the ‘join()’ method as shown below.

from time import sleep
from threading import *
#thread1
class FACE(Thread):
    def run(self):
        for i in range(3):
            print("FACE Prep")
            sleep(1)
#thread2
class Python(Thread):
    def run(self):
        for i in range(3):
            print("Python")
            sleep(1)

#main() method
t1 = FACE()
t2 = Python()
t1.start()
t2.start()
t1.join()  #waiting for t1 to complete its execution
t2.join()  #waiting for t2 to complete its execution
print("Bye")  
Output:
FACE Prep
Python
FACE Prep
Python
FACE Prep
Python
Bye

This is all about the basics of Multithreading.

Multithreading in Python FAQs

The process of performing multiple tasks using a thread is called multithreading. 

Multithreading in Python can be achieved by importing the built-in module named ‘threading’.

We can make threads to sleep for a particular second using the class ‘sleep’ which is in the module ‘time’.

We can start the execution of a thread by using the method ‘start()’.