A thread is the smallest sequence of programmed instructions processed without interruptions by a CPU. Multithreading refers to the execution of threads in parallel, which in turn allows simultaneous execution of multiple applications and multiple tasks within an application. Thanks to multithreading, you can run Microsoft Word and a PDF reader, and many other applications simultaneously. Similarly, within Microsoft Word, multiple tasks can be performed in parallel using threading. For instance, while you are writing, spell checker is consistently executed in the background which keeps correcting your spelling mistakes.
Like all modern programming languages, Python also allows you to implement multithreading in your applications. In this article, you’ll see how you can implement multithreading with Python and we’ll show examples of how multithreading can be used to run multiple functions in parallel. Let’s first see what happens when you don’t implement multithreading in your code.
Python Script Without Multithreading
In this section, you’ll see how Python code executes when you don’t implement multithreading. This is the “normal” behavior of many programming languages, including Python. In the next section, we’ll show you how to run functions in parallel using multithreading and how multithreading impacts the behavior of your code.
The following script creates three classes. Each of the classes contain one function named run()
which executes a for loop that prints a simple string 5 times on a system’s console.
class App1:
def run(self):
for i in range(5):
print("thread 1")
class App2:
def run(self):
for i in range(5):
print("thread 2")
class App3:
def run(self):
for i in range(5):
print("thread 3")
The following script creates objects of the three classes:
app1 = App1()
app2 = App2()
app3 = App3()
Let’s now call the run()
functions using objects of the three classes.
app1.run()
app2.run()
app3.run()
The output below shows that the code runs in a sequence since there is no threading. First the run()
method of the run()
function of class
The run()
function of the run()
function from the run()
function from the run()
function from the
Output:
thread 1
thread 1
thread 1
thread 1
thread 1
thread 2
thread 2
thread 2
thread 2
thread 2
thread 3
thread 3
thread 3
thread 3
thread 3
Running Functions in Parallel with Multithreading
In the previous section, you saw that the three run()
functions from the
To implement threading in Python, you have to perform three steps:
- Inherit the class that contains the function you want to run in a separate thread by using the Thread class.
- Name the function you want to execute in a thread
run()
. - Call the
start()
function from the object of the class containing therun()
method. Thestart()
function calls therun()
method by default.
The following script inherits the
from threading import *
class App1(Thread):
def run(self):
for i in range(5):
print("thread 1")
class App2(Thread):
def run(self):
for i in range(5):
print("thread 2")
class App3(Thread):
def run(self):
for i in range(5):
print("thread 3")
Once that’s set up, create objects of the three classes, like this:
app1 = App1()
app2 = App2()
app3 = App3()
Instead of calling the start()
function, let’s see what happens if we call the run()
functions.
app1.run()
app2.run()
app3.run()
Output:
thread 1
thread 1
thread 1
thread 1
thread 1
thread 2
thread 2
thread 2
thread 2
thread 2
thread 3
thread 3
thread 3
thread 3
thread 3
The output is identical to the output when we executed the three run()
functions in a sequence. As we said earlier, to execute a function in a thread, you need to call the start()
method. Behind the scenes, the start()
method executes each run()
method in a separate thread. Okay, let’s now execute the following script.
app1.start()
app2.start()
app3.start()
When you run the script above, four threads execute in parallel. The first thread is the main thread which is by default created when you run any Python script. When the runtime reaches the statement app1.start()
inside the main thread, a new thread is created which starts running in parallel with the main thread. Inside the main thread, the executions of the statements app2.start()
and app3.start()
create two more threads which run in parallel with the main thread and the previously created thread for the run()
function.
Now the output shows that the result are not in a sequence. You still may see that you have multiple strings printed by the run()
function of run()
function of run()
functions are so small that multiple iterations of for loop are executed by one thread before the console output is accessed by other threads running in parallel.
We ran this multithreaded Python code on two different systems and got these two outputs:
Output 1:
thread 1thread 2
thread 2
thread 2
thread 2
thread 2
thread 1
thread 1
thread 1
thread 1
thread 3
thread 3
thread 3
thread 3
thread 3
Output 2:
thread 1thread 3thread 2
thread 1thread 3thread 2
thread 1thread 3thread 2
thread 1thread 3thread 2
thread 1thread 3thread 2
If you want only one iteration of the for loop to execute inside the run()
function before the console output is accessed by the other threads, you can add a slight delay with the sleep
function. In the following script, a delay of 1 second is added inside the for loops of each run()
function. This will halt the execution of the for loops for one second after every execution. This kind of defeats the purpose of multithreading but it helps prove our codes are running multiple threads at the same time.
from threading import *
from time import *
class App1(Thread):
def run(self):
for i in range(5):
print("thread 1")
sleep(1)
class App2(Thread):
def run(self):
for i in range(5):
print("thread 2")
sleep(1)
class App3(Thread):
def run(self):
for i in range(5):
print("thread 3")
sleep(1)
app1 = App1()
app2 = App2()
app3 = App3()
app1.start()
app2.start()
app3.start()
In the output below you can see that now after every iteration, the console is assigned to another thread and hence, you can see one iteration of the for loop from each thread. The following output clearly shows that the three threads are running in parallel.
Output:
thread 1
thread 2
thread 3
thread 1
thread 2
thread 3
thread 1
thread 2
thread 3
thread 1
thread 2
thread 3
thread 1
thread 2
thread 3
Code More, Distract Less: Support Our Ad-Free Site
You might have noticed we removed ads from our site - we hope this enhances your learning experience. To help sustain this, please take a look at our Python Developer Kit and our comprehensive cheat sheets. Each purchase directly supports this site, ensuring we can continue to offer you quality, distraction-free tutorials.
Waiting for the Child Threads to Finish execution
As we mentioned earlier, when you call the start()
method on the objects of run()
functions from the
app1 = App1()
app2 = App2()
app3 = App3()
app1.start()
app2.start()
app3.start()
print("\nhello")
Output:
thread 1
thread 2
thread 3
hello
thread 1
thread 2
thread 3
thread 1
thread 2
thread 3
thread 1
thread 2
thread 3
thread 1
thread 2
thread 3
As expected, since the main thread and the three child threads execute in parallel, the string printed by the main thread is displayed on the console before the three child threads complete their executions. This is rarely how you want your code to behave.
To wait for the complete execution of a thread before another thread resumes, use the join()
method. For instance, in the following script, the main thread waits for the three child threads to complete their execution before resuming its execution and printing the “hello” string on the console.
app1 = App1()
app2 = App2()
app3 = App3()
app1.start()
app2.start()
app3.start()
app1.join()
app2.join()
app3.join()
#this will be printed after the three child threads
print("\nhello")
The output now shows that the string printed by the main thread is displayed at the end of the console output after the output from the three child threads are displayed.
Output:
thread 1
thread 2
thread 3
thread 1
thread 2
thread 3
thread 1
thread 2
thread 3
thread 1
thread 2
thread 3
thread 1
thread 2
thread 3
hello
Running Custom Functions in Threads
In the previous section, you saw that the start()
function by default executes functions named run()
. What if you want to name your function something other than run()
? Also, what do you do if you want to pass arguments to a function that you want to execute in a thread? Fortunately, the Thread class allows this, as well.
To run a custom function with parameters, you need to create an object of the Thread class from the threading module. In the class constructor, you need to pass the function name you want to execute in a thread to the “target” parameter. The arguments are passed in the form of a tuple to the “args” parameter.
In the following script, you create a class my_function()
. The function accepts one parameter, name. To execute the my_function()
function in a thread, create an object of the my_function()
name is passed again to another Thread class constructor, however, this time the argument value is changed to “beta”. To execute both the threads, you simply need to call the start()
methods on the corresponding Thread class objects.
from threading import *
from time import *
class App1(Thread):
def my_function(self, name):
for i in range(5):
print("thread "+ str(name))
sleep(1)
app1 = App1()
t1 = Thread(target=app1.my_function, args=("alpha",))
t1.start()
t2 = Thread(target=app1.my_function, args=("beta",))
t2.start()
Output:
thread alpha
thread beta
thread alpha
thread beta
thread alpha
thread beta
thread alpha
thread beta
thread alpha
thread beta
You can see that both threads execute in parallel. One thread calls the my_function()
using “alpha” as the value for the function’s name parameter, while the other thread passes “beta” as the value for the function’s name parameter.
To see different ways of running functions in parallel, try our tutorial on python asynchronous programming with asyncio.
Code More, Distract Less: Support Our Ad-Free Site
You might have noticed we removed ads from our site - we hope this enhances your learning experience. To help sustain this, please take a look at our Python Developer Kit and our comprehensive cheat sheets. Each purchase directly supports this site, ensuring we can continue to offer you quality, distraction-free tutorials.