Python multithreading tutorial

In this Python multithreading tutorial, you will see different ways to create threads and learn how to synchronize thread safe operations. Each section of this article contains an example and sample code to step through the concept.

By the way, multithreading is the core concept of software programming supported by almost all high-level programming languages. So the first thing you should know is: what is threading and what multithreading means in computer science.

What is threading in computer science?

In software programming, thread is the smallest execution unit with independent instruction set. It is part of a process and runs in the same context that shares the program's runnable resources, such as memory. A thread has a starting point, an execution order, and a result. It has an instruction pointer, which is used to save the current state of the thread and control the next execution order.

What is multithreading in computer science?

The ability of a process to execute multiple threads in parallel is called multithreading. Ideally, multithreading can significantly improve the performance of any program. Moreover, Python multithreading mechanism is very user-friendly, and you can learn quickly.

Advantages of multithreading

  • Multithreading can significantly improve the computing speed of multiprocessor or multi-core systems, because each processor or core processes a separate thread at the same time.
  • Multithreading allows programs to remain responsive while one thread waits for input while another thread runs the GUI. This statement applies to multiprocessor or uniprocessor systems.
  • All threads of a process can access its global variables. If a global variable changes in one thread, it is also visible to other threads. Threads can also have their own local variables.

Disadvantages of multithreading

  • On a single processor system, multithreading does not affect computing speed. Performance may degrade due to the overhead of managing threads.
  • Synchronization is required when accessing shared resources to prevent mutual exclusion. It directly leads to more memory and CPU utilization.
  • Multithreading increases the complexity of the program, which also makes debugging difficult.
  • It increases the possibility of potential deadlocks.
  • When a thread cannot access a shared resource regularly, it may cause hunger. The application will not be able to resume its work.

So far, you have read about the theoretical concept of threads. If you are not familiar with Python, we recommend that you read our 30 quick Python coding tips, which can also help you write Python multithreaded code. Many of our readers use these techniques and can improve their coding skills.

Python multithreading module

Python provides two modules to implement threads in programs.

Module and
< thread > module.

Note: for your reference, python 2 X once had a < thread > module. But it's in Python 3 Deprecated in X and renamed to <_ Thread > module for backward compatibility.

The main difference between the two modules is the module<_ Threads > implement threads as functions. On the other hand, < threading > module provides an object-oriented method to enable thread creation.

How to create a thread using the thread module?

If you decide to apply the < thread > module in your program, use the following method to generate threads.

#grammar

thread.start_new_thread ( function, args[, kwargs] )

This method is very effective and direct for creating threads. You can use it to run programs on Linux and Windows.

This method starts a new thread and returns its identifier. It calls the function specified as the function parameter using the passed parameter list. When < function > returns, the thread silently exits.

Here, args is a parameter tuple; Call < function > with empty tuples without any parameters. The optional < kwargs > parameter specifies the dictionary of keyword parameters.

**If < function > terminates due to an unhandled exception, the stack trace is printed and the thread exits (it does not affect other threads, they continue to run). Use the following code to learn more about threads.

Basic Python multithreading example

#Python multithreading example.
#1. Use recursion to calculate factorial.
#2. Use thread to call factorial function.

from _thread import start_new_thread
from time import sleep

threadId = 1 #Thread counter
waiting = 2 #2 seconds 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 a local Python terminal or use any online Python terminal. After executing this program, it will produce the following output.

Program output

# Python multithreading: program output-
Wait for the thread to return...

Thread: 1
1 != 1
2 != 2
3 != 6
4 != 24
5 != 120

Thread: 2
1 != 1
2 != 2
3 != 6
4 != 24

How to create a thread using the thread module?

The latest < threading > module provides rich features and better thread support than the legacy < thread > module discussed in the previous section< The threading > module is a good example of Python mu lt ithreading.
The < threading > module combines all the methods of the < thread > module and exposes some additional methods

  • threading.activeCount(): it finds the total number. The active thread object.
  • threading.currentThread(): you can use it to determine the number of Thread objects in the caller's thread control.
  • threading.enumerate(): this will give you a complete list of currently active thread objects.

In addition to the above methods, the < threading > module also provides the < Thread > class. You can try to implement threads. It is an object-oriented variant of Python mu lt ithreading.

Steps to implement threads using thread module

You can implement a new thread using the < threading > module as follows.

  • Construct a subclass from the < Thread > class.
  • Override the < init(self [,args]) > method to provide parameters as required.
  • Next, rewrite the < run(self [,args]) > method to write the thread's business logic.

Once a new < thread > subclass is defined, it must be instantiated to start a new thread. Then, call the < start () > method to start it. It will eventually call the < run() > method to execute the business logic.

Example – create a thread class to print dates

#The Python multithreaded example prints the current date.
#1. Using threading The thread class defines subclasses.
#2. Instantiate subclasses and trigger threads.

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 a new thread
thread1 = myThread("Thread", 1)
thread2 = myThread("Thread", 2)

# Start a new thread
thread1.start()
thread2.start()

thread1.join()
thread2.join()
print("\nExiting the Program!!!")

Program output

Starting Thread
Thread[1]: 2021-07-22
Exiting Thread

Starting Thread
Thread[2]: 2021-07-22
Exiting Thread

Exiting the Program!!!

Python multithreading -- synchronous threads

The < threading > module has a bui lt-in function to implement locking, allowing you to synchronize threads. Locks are needed to control access to shared resources to prevent corruption or loss of data.
You can call the Lock() method to apply the lock, which returns the new lock object. You can then call the get (block) method of the lock object to force the threads to run synchronously.
The optional blocking parameter specifies whether the thread is waiting to acquire a lock.

  • Case} Blocking = 0: if the lock acquisition fails, the thread will immediately return a value of zero. If the lock succeeds, it will return a value of one.
  • 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 needed.

For reference only, Python's built-in data structures (such as lists, dictionaries) are thread safe because it has the side effects of the atomic bytecode used to manipulate them. Other data structures or basic types implemented in Python, such as integers and floating-point numbers, do not have this protection. To prevent simultaneous access to an object, we use a Lock object.

Locked multithreading example

#Python multithreading example to demonstrate locking.
#1. Using threading The thread class defines subclasses.
#2. Instantiate subclasses and trigger threads.
#3. Implement the lock in the run method of the thread.

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)
        # Get lock synchronization thread
        threadLock.acquire()
        print_date(self.name, self.counter)
        # Release the 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 a new thread
thread1 = myThread("Thread", 1)
thread2 = myThread("Thread", 2)

# Start a new thread
thread1.start()
thread2.start()

# Add thread 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]: 2021-07-22
Exiting Thread

Starting Thread
Thread[2]: 2021-07-22
Exiting Thread

Exiting the Program!!!

Keywords: Python

Added by biscutty on Sat, 08 Jan 2022 05:26:08 +0200