Multi-threading in Python - Tutorial


Multi-threading in Python

Multithreading is a concept of executing different pieces of code concurrently. A thread is an entity that can run on the processor individually with its own unique identifier, stack, stack pointer, program counter, state, register set and pointer to the Process Control Block of the process that the thread lives on.

In this tutorial, we will learn with examples how to do multithreading in Python programming.

threading module is used to achieve multithreading in Python.


Create a Thread

To create a thread, you can use threading.Thread() class. The syntax of the Thread() class is:

threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
  • leave group as None.
  • target is the callable object to be invoked by the run() method of Thread.
  • name is the Thread name that you can provide and refer to later in the program.
  • args is the argument tuple for the target invocation.
  • kwargs is a dictionary of keyword arguments for the target invocation.
  • daemon if set to True, will make the thread a daemon thread, meaning it will not block the program from exiting.

Start a Thread

Once you have created a thread using the Thread() class, you can start it using the start() method.

t1 = threading.Thread()
t1.start()

Explanation:

  1. The Thread class is instantiated to create a new thread object.
  2. Then, start() is called on this object to begin its execution in parallel with the main thread.

Wait Until the Thread is Finished

You can make the main thread wait until a specific thread is finished using the join() method.

t1.join()

Explanation:

  1. The join() method ensures that the main program waits for t1 to complete before moving on to the next statement.
  2. This is useful when you want to ensure that all threads are completed before the main thread terminates.

Example - Python Multi-threading with Two Threads

In the following example, we will perform multi-threading with two threads. Each thread runs in parallel to the other and is executed whenever processing resources are available to it.

To demonstrate this behavior, we will run a function in each thread that prints ones and twos.

Python Program

import threading 

def print_one(): 
	for i in range(10):
		print(1)

def print_two(): 
	for i in range(10):
		print(2)

if __name__ == "__main__": 
	# create threads
	t1 = threading.Thread(target=print_one) 
	t2 = threading.Thread(target=print_two) 

	# start thread 1 
	t1.start() 
	# start thread 2 
	t2.start() 

	# wait until thread 1 is completely executed 
	t1.join() 
	# wait until thread 2 is completely executed 
	t2.join() 
	# both threads completely executed 

	print("Done!")

Explanation:

  1. The two functions print_one and print_two are defined to print the numbers 1 and 2, respectively.
  2. Two thread objects t1 and t2 are created, each targeting one of the functions.
  3. We start both threads using start(), allowing them to run concurrently.
  4. Then, we use join() to ensure the main thread waits until both threads finish execution before printing Done!.

Output

1
1
1
2
2
2
1
1
2
1
2
2
2
2
2
2
1
1
1
1
Done!

Example - Multi-threading with Arguments Passed to Threads

In this example, we will pass arguments to the threads. Also, we will demonstrate calling the same target function for different threads with varying arguments.

Python Program

import threading 

def print_x(x, n): 
	for i in range(n):
		print(x)

if __name__ == "__main__": 
	# create threads
	t1 = threading.Thread(target=print_x, args=(1, 5)) 
	t2 = threading.Thread(target=print_x, args=(2, 10)) 

	# start thread 1 
	t1.start() 
	# start thread 2 
	t2.start() 

	# wait until thread 1 is completely executed 
	t1.join() 
	# wait until thread 2 is completely executed 
	t2.join() 
	# both threads completely executed 

	print("Done!")

Explanation:

  1. The function print_x is defined to print the value x, n times.
  2. Threads t1 and t2 are created, each passing different arguments to the function (1 with 5 iterations, and 2 with 10 iterations).
  3. Each thread is started using start() and the main thread waits for them to finish using join().
  4. The output shows interleaved printing of ones and twos, depending on thread scheduling by the operating system.

Output

1
1
1
2
2
2
2
1
2
1
2
2
2
2
2
Done!

Example - Daemon Threads

Daemon threads are threads that run in the background and are killed automatically when the main program exits, even if they haven't finished executing. These threads are useful for tasks like logging or background monitoring where you don't need to wait for them to finish before the program terminates.

Python Program

import threading 

def background_task(): 
	for i in range(5):
		print(f'Background task {i}')

if __name__ == "__main__": 
	# create daemon thread
	t1 = threading.Thread(target=background_task, daemon=True) 
	# start thread
	t1.start() 

	# main program finishes here
	print('Main program finished!')

Explanation:

  1. The background_task function runs a simple loop printing background task messages.
  2. A daemon thread is created by setting daemon=True when creating the thread.
  3. The thread starts running, but the main program terminates immediately after printing Main program finished!.
  4. Since t1 is a daemon thread, it will be automatically stopped when the main program exits.

Output

Background task 0
Background task 1
Background task 2
Background task 3
Background task 4
Main program finished!

Summary

In this tutorial, we learned how to implement multi-threading in Python programming with detailed examples. We demonstrated how to create threads, start them, wait for them to finish, pass arguments, and work with daemon threads for background tasks.




Python Libraries