In this Python multithreading tutorial, you’ll get to see different methods to create threads and learn to implement synchronization for thread-safe operations. Each section of this post includes an example and the sample code to explain the concept step by step.
Introduction to multithreading
Multithreading is a core concept of software programming that almost all high-level programming languages support. Hence, the first things you should know are: What is a thread and What does multithreading mean in Computer Science?
What is a Thread in Computer Science?
In software programming, a thread is the smallest unit of execution with an independent set of instructions. It is a part of the process and operates in the same context sharing program’s runnable resources like memory. A thread has a starting point, an execution sequence, and a result. It has an instruction pointer that holds the current state of the thread and controls what executes next in what order.
What is multithreading in Computer Science?
The ability of a process to execute multiple threads parallelly is called multithreading. Ideally, multithreading can significantly improve the performance of any program. And Python multithreading mechanism is pretty user-friendly, which you can learn quickly.
Advantages of Multithreading
- Multithreading can significantly improve the speed of computation on multiprocessor or multi-core systems because each processor or core handles a separate thread concurrently.
- Multithreading allows a program to remain responsive while one thread waits for input, and another runs a GUI at the same time. This statement holds true for both multiprocessor and single-processor systems.
- All the threads of a process have access to its global variables. If a global variable changes in one thread, it is visible to other threads as well. A thread can also have its own local variables.
Disadvantages of Multithreading
- On a single-processor system, multithreading won’t hit the speed of computation. The performance may downgrade due to the overhead of managing threads.
- Synchronization is needed to prevent mutual exclusion while accessing shared resources. It directly leads to more memory and CPU utilization.
- Multithreading increases the complexity of the program, thus also making it difficult to debug.
- It raises the possibility of potential deadlocks.
- It may cause starvation when a thread doesn’t get regular access to shared resources. The application would then fail to resume its work.
Well so far, you’ve read the theoretical concepts about threads. If you are new to Python, we would suggest you go through our 30 quick Python coding tips that can help you in writing Python multithreading code as well. Many of our readers have used these tips and were able to improve their coding skills.
Understand multithreading in Python
Python offers two modules to implement threads in programs.
- <thread> module and
- <threading> module.
Note: For your information, Python 2 used to have the <thread> module. But it got deprecated in Python 3 and renamed to <_thread> module for backward compatibility.
The principal difference between the two modules is that the module <_thread> implements a thread as a function. On the other hand, the module <threading> offers an object-oriented approach to enable thread creation.
Using the thread module to create threads
If you decide on the <thread> module to apply in your program, then use the following method to spawn threads.
#Syntax thread.start_new_thread ( function, args[, kwargs] )
This method is quite efficient and straightforward for creating threads. You can use it to run programs in both Linux and Windows.
This method starts a new thread and returns its identifier. It’ll invoke the function specified as the “function” parameter with the passed list of arguments. When the <function> returns, the thread would silently exit.
Here, args is a tuple of arguments; use an empty tuple to call <function> without any arguments. The optional <kwargs> argument specifies the dictionary of keyword arguments.
If the <function> terminates with an unhandled exception, a stack trace is printed and then the thread exits (It doesn’t affect other threads, they continue to run). Use the below code to learn more about threading.
Basic Python multithreading example
#Python multithreading example. #1. Calculate factorial using recursion. #2. Call factorial function using thread. from _thread import start_new_thread from time import sleep threadId = 1 # thread counter waiting = 2 # 2 sec. waiting time def factorial(n): global threadId rc = 0 if n < 1: # base case print("{}: {}".format('\nThread', threadId )) threadId += 1 rc = 1 else: returnNumber = n * factorial( n - 1 ) # recursive call print("{} != {}".format(str(n), str(returnNumber))) rc = returnNumber return rc start_new_thread(factorial, (5, )) start_new_thread(factorial, (4, )) print("Waiting for threads to return...") sleep(waiting)
You can run the above code in your local Python terminal or use any online Python terminal. Once you execute this program, it’ll produce the following output.
Program output
# Python multithreading: program output- Waiting for threads to return... Thread: 1 1 != 1 2 != 2 3 != 6 4 != 24 5 != 120 Thread: 2 1 != 1 2 != 2 3 != 6 4 != 24
You can quickly find and choose the online Python terminal from the below article on our blog. Here, we’ve compiled a list of 7 best online Python terminals to test and run your code on the fly. We’ve tested all the Python multithreading examples source code with these online terminals as well.
Check out – Seven Best Online Python Interpreters
Using the threading module to create threads
The latest <threading> module provides rich features and better support for threads than the legacy <thread> module discussed in the previous section. The <threading> module is an excellent example of Python Multithreading.
The <threading> module combines all the methods of the <thread> module and exposes a few additional methods.
threading.activeCount()
: It finds the total number of active thread objects.threading.currentThread()
: You can use it to determine the number of thread objects in the caller’s thread control.threading.enumerate()
: It will give you a complete list of thread objects that are currently active.
Apart from the above methods, the <threading> module also presents the <Thread> class that you can try for implementing threads. It is an object-oriented variant of Python multithreading.
The <Thread> class publishes the following methods.
Class Methods | Method Description |
---|---|
run(): | It is the entry point function for any thread. |
start(): | The start() method triggers a thread when the run method gets called. |
join([time]): | The join() method enables a program to wait for threads to terminate. |
isAlive(): | The isAlive() method verifies an active thread. |
getName(): | The getName() method retrieves the name of a thread. |
setName(): | The setName() method updates the name of a thread. |
If you wish, you can refer to the native Python docs to dig deeper into the <threading> module functionality.
Steps to implement threads using the threading module
You may follow the below steps to implement a new thread using the <threading> module.
- Construct a subclass from the <Thread> class.
- Override the
<__init__(self [,args])>
method to supply arguments as per requirements. - Next, override the
<run(self [,args])>
method to code the business logic of the thread.
Once you define the new <Thread> subclass, you have to instantiate it to start a new thread. Then, invoke the <start()> method to initiate it. It will eventually call the <run()> method to execute the business logic.
Example – Create a thread class to print the date
#Python multithreading example to print current date. #1. Define a subclass using threading.Thread class. #2. Instantiate the subclass and trigger the thread. import threading import datetime class myThread (threading.Thread): def __init__(self, name, counter): threading.Thread.__init__(self) self.threadID = counter self.name = name self.counter = counter def run(self): print("\nStarting " + self.name) print_date(self.name, self.counter) print("Exiting " + self.name) def print_date(threadName, counter): datefields = [] today = datetime.date.today() datefields.append(today) print("{}[{}]: {}".format( threadName, counter, datefields[0] )) # Create new threads thread1 = myThread("Thread", 1) thread2 = myThread("Thread", 2) # Start new Threads thread1.start() thread2.start() thread1.join() thread2.join() print("\nExiting the Program!!!")
Program output
Starting Thread Thread[1]: 2019-04-28 Exiting Thread Starting Thread Thread[2]: 2019-04-28 Exiting Thread Exiting the Program!!!
Also Read: Socket Programming in Python
Synchronizing threads / Locks in Python
The <threading> module has built-in functionality to implement locking that allows you to synchronize threads. Locking is required to control access to shared resources to prevent corruption or missed data.
You can call the Lock() method to apply locks, it returns the new lock object. Then, you can invoke the acquire(blocking) method of the lock object to enforce threads to run synchronously.
The optional blocking parameter specifies whether the thread waits to acquire the lock.
- Case blocking = 0: The thread would return immediately with a zero value if it fails to acquire the lock and with a one if the lock was successful.
- Case blocking = 1: The thread blocks and waits for the lock to be released.
The release() method of the lock object is used to release the lock when it is no longer required.
Just for your information, Python’s built-in data structures such as lists, and dictionaries are thread-safe as a side-effect of having atomic byte codes for manipulating them. Other data structures implemented in Python or basic types like integers and floats, don’t have that protection. To guard against simultaneous access to an object, we use a Lock object.
Multithreading example for locking
#Python multithreading example to demonstrate locking. #1. Define a subclass using threading.Thread class. #2. Instantiate the subclass and trigger the thread. #3. Implement locks in thread's run method. import threading import datetime exitFlag = 0 class myThread (threading.Thread): def __init__(self, name, counter): threading.Thread.__init__(self) self.threadID = counter self.name = name self.counter = counter def run(self): print("\nStarting " + self.name) # Acquire lock to synchronize thread threadLock.acquire() print_date(self.name, self.counter) # Release lock for the next thread threadLock.release() print("Exiting " + self.name) def print_date(threadName, counter): datefields = [] today = datetime.date.today() datefields.append(today) print("{}[{}]: {}".format( threadName, counter, datefields[0] )) threadLock = threading.Lock() threads = [] # Create new threads thread1 = myThread("Thread", 1) thread2 = myThread("Thread", 2) # Start new Threads thread1.start() thread2.start() # Add threads to thread list threads.append(thread1) threads.append(thread2) # Wait for all threads to complete for thread in threads: thread.join() print("\nExiting the Program!!!")
Program output
Starting Thread Thread[1]: 2019-04-28 Exiting Thread Starting Thread Thread[2]: 2019-04-28 Exiting Thread Exiting the Program!!!
Practice quiz on multithreading
For practice, you should take up the below questionnaire. It would help you evaluate what you have learned from this tutorial.
However, you can also work on various Python exercises to boost your programming skills.
Summary – Python multithreading for beginners
We hope that you will find this Python Multithreading tutorial very interesting and helpful. The illustrations you found here will surely help in uplifting your Python skills.
If you liked this post, then please distribute it among your friend circle or on the social media platforms you use.
Keep learning,
TechBeamers