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, App2, App3.

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 App1 class prints the string “thread 1” five times on the console. After this is done, the run() function of class App2 prints the string “thread 2” then , App3 prints “thread 3” to the console.

The run() function of the App2 class will not execute until the execution of the run() function from the App1 is finished. Similarly, the run() function from the App3 class does not run before the execution of the run() function from the App2 class completes.

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 App1, App2, and App3 classes ran sequentially. This is probably the behavior you’re used to seeing, but what if you want to execute these three functions in parallel? This is where threading comes into play.

To implement threading in Python, you have to perform three steps:

  1. Inherit the class that contains the function you want to run in a separate thread by using the Thread class.
  2. Name the function you want to execute in a thread run().
  3. Call the start() function from the object of the class containing the run() method. The start() function calls the run() method by default.

The following script inherits the App1, App2, and App3classes from the Thread class. You must import the threading module in order to access the Thread class, which is what we do in the first line of the script.

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 App1 class’ 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 App2 class before the run() function of App3 class executes, but there’s a reason for that. The threads are being executed in parallel, but the scripts in the 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

Get Our Python Developer Kit for Free

I put together a Python Developer Kit with over 100 pre-built Python scripts covering data structures, Pandas, NumPy, Seaborn, machine learning, file processing, web scraping and a whole lot more - and I want you to have it for free. Enter your email address below and I'll send a copy your way.

Yes, I'll take a free Python Developer Kit

Waiting for the Child Threads to Finish execution

As we mentioned earlier, when you call the start() method on the objects of App1, App2, and App3 classes, four threads execute in parallel: the main thread which runs the overall Python script, and three child threads which execute run() functions from the App1, App2, and App3 classes. In the following script, after starting the three threads, you print a simple string on the console. This string runs inside the main thread.

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 App1 with one function my_function(). The function accepts one parameter, name. To execute the my_function() function in a thread, create an object of the App1 class. Next, the function is passed to the “target” parameter of the Thread class using the App1 class object. The parameter value “alpha” is passed to the “args” tuple. 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.


Get Our Python Developer Kit for Free

I put together a Python Developer Kit with over 100 pre-built Python scripts covering data structures, Pandas, NumPy, Seaborn, machine learning, file processing, web scraping and a whole lot more - and I want you to have it for free. Enter your email address below and I'll send a copy your way.

Yes, I'll take a free Python Developer Kit