Multithreading in Python

Harsh S.
By
Harsh S.
Hello, I'm Harsh, I hold a degree in Masters of Computer Applications. I have worked in different IT companies as a development lead on many large-scale...
13 Min Read
Python Multithreading - Advanced Python Concepts

In this tutorial, you’ll learn about multithreading in Python and how to create a thread in Python using the threading module. We’ll also cover thread synchronization with Python locks to manage shared resources efficiently. Each section includes step-by-step explanations with examples and sample code to help you understand the concepts easily.

Multithreading in Python: Everything You Need to Know

Multithreading is an essential concept in software programming, allowing multiple threads to run concurrently within a process. Most high-level languages, including Python, support multithreading to improve performance.

What is a Thread in Python?

In software programming, a thread is the smallest unit of execution within a process. It operates in the same memory space as other threads in the process, allowing efficient task execution. In Python, threads are managed using the threading module, which simplifies concurrent programming.

What is Multithreading in Python?

Multithreading allows a single process to execute multiple threads simultaneously. This can significantly improve performance for I/O-bound tasks, such as reading files, making API requests, or handling user input. Python’s threading module provides an easy way to create and manage threads, making it beginner-friendly.

Pros and Cons of Multithreading in Python

Multithreading lets your Python programs do more than one thing at the same time. But it has its ups and downs. Let’s break them down.

Advantages of Multithreading:

  • Faster on Multi-Core Machines: When your computer has several cores or processors, multithreading can speed up your program. Each thread can run on a different core, making things run faster.
  • Keeps Your Program Responsive: With multithreading, one thread can wait for user input while another keeps the program running (like updating a screen). This helps especially with programs that have a user interface.
  • Easy Data Sharing: All threads in a program share the same global variables. If one thread changes a global variable, the other threads see that change immediately.

    Disadvantages of Multithreading:

    • Not Much Faster on Single-Core Systems: On computers with just one processor, multithreading might actually slow your program down because the extra work to manage threads can get in the way.
    • Extra Work to Manage Data (Synchronization): When several threads use the same data, you need to use locks or other tools to make sure they don’t mess things up. This extra work can use more memory and CPU time.
    • More Complex Code: Programs with many threads are harder to write and debug. It’s easier to make mistakes, like having two threads wait for each other forever (a deadlock) or one thread not getting enough time to run (starvation).

      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.

      Check out the below topic: Top 30 Python Tips 👉

        Multithreading in Python with Examples

        Python gives you two modules to work with threads:

        • <_thread> module
        • <threading> module

        Note: In Python 2, there was a module called thread, but in Python 3 it’s renamed to _thread for backward compatibility.

        The main difference is:

        • The _thread module lets you create a thread by calling a function.
        • The threading module uses an object-oriented approach to create and manage threads.

        Using ‘_thread’ Module to Create Threads in Python

        If you decide to use the _thread module in your program, you can create threads with the following method.

        #Syntax

        _thread.start_new_thread(function, args[, kwargs])
        • function: The function that the thread will run.
        • args: A tuple of arguments to pass to that function. Use an empty tuple () if your function does not need any arguments.
        • kwargs (optional): A dictionary of keyword arguments to pass to the function.

        This method starts a new thread and returns its identifier. The thread will run the specified function with the given arguments. When the function finishes, the thread exits silently.

        If the function ends with an unhandled error, a stack trace is printed for that thread, but the other threads keep running. This method works well on both Linux and Windows systems.

        Python Multithreading Example Using _thread Module

        from _thread import start_new_thread
        from time import sleep

        threadId = 1 # thread counter
        waiting = 2 # waiting time in seconds

        def factorial(n):
        global threadId
        if n < 1: # base case
        print("{}: {}".format('\nThread', threadId))
        threadId += 1
        return 1
        else:
        result = n * factorial(n - 1) # recursive call
        print("{} != {}".format(n, result))
        return result

        # Start two threads running the factorial function
        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

        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 run this code in your local Python terminal or any online Python terminal. For more options, check out our article listing 7 of the best online Python terminals to test and run your code quickly.

        Check out – Seven Best Online Python Interpreters

        Using ‘threading’ Module to Create Threads in Python

        The modern threading module in Python is a better and more powerful way to work with threads compared to the old _thread module. It gives you extra features and a cleaner, object-oriented approach.

        It combines all the methods of the old _thread module and exposes a few additional methods.

        • threading.activeCount(): Returns the total number of active threads.
        • threading.currentThread(): Gives you the thread object that is currently running.
        • threading.enumerate(): Returns a list of all active thread objects.

        Apart from the above methods, the threading module also provides the Thread class. This class lets you create threads using an object-oriented method. Here are some its important methods:

        Class MethodsMethod Description
        run():It is the entry point function for any thread.
        start():Starts the thread, which then calls the run() method automatically.
        join([time]):Waits for the thread to finish. You can also specify a timeout.
        isAlive():Checks if the thread is still running.
        getName():Returns the name of the thread.
        setName():Sets or changes the name of the thread.
        Functions of the Python’s thread class

        For more details, you can check the official Python documentation.

        Python Multithreading Example Using threading module

        You may follow the below steps to implement a new thread:

        • 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

        Output:


        Starting Thread
        Thread[1]: 2025-02-10
        Exiting Thread

        Starting Thread
        Thread[2]: 2025-02-10
        Exiting Thread

        Exiting the Program!!!

        Also Read: Socket Programming in Python

        Using Locks for Synchronizing Threads 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 using Locks in Python

        #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

        Output:

        Starting Thread
        Thread[1]: 2025-02-10
        Exiting Thread

        Starting Thread
        Thread[2]: 2025-02-10
        Exiting Thread

        Exiting the Program!!!

        Practice Quiz on Multithreading in Python

        For practice, you should take up the below questionnaire. It would help you evaluate what you have learned from this tutorial.

        Check out the below topic: Python Quiz on Threading 👉

          However, you can also work on various Python exercises to boost your programming skills.

          Conclusion: Multithreading in Python

          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 like (@techbeamers) you use.

          Keep learning,
          TechBeamers

          Share This Article
          Leave a Comment

          Leave a Reply

          Your email address will not be published. Required fields are marked *