Python Multithreading

Introduction

Multithreading in Python allows you to run multiple threads (smaller units of a process) concurrently, making your program more efficient and responsive, especially for I/O-bound and high-level structured network code. Python provides the threading module to work with threads.

  • Thread: The smallest unit of execution within a process.
  • Multithreading: Running multiple threads concurrently within a single process.
  • The threading module: Multithreading in Python can be achieved using the threading module, which provides a high-level interface for creating and managing threads.

The threading Module

The threading module provides a higher-level interface for working with threads.

Creating a Thread

You can create a thread by creating an instance of the Thread class and passing a target function that the thread will execute.

Example

import threading

def print_numbers():
    for i in range(1, 6):
        print(i)

# Create a thread
thread = threading.Thread(target=print_numbers)

# Start the thread
thread.start()

# Wait for the thread to complete
thread.join()

print("Thread execution completed.")

Output

1
2
3
4
5
Thread execution completed.

Threading with Arguments

You can pass arguments to the target function using the args parameter.

Example

import threading

def print_numbers(n):
    for i in range(1, n+1):
        print(i)

# Create a thread
thread = threading.Thread(target=print_numbers, args=(5,))

# Start the thread
thread.start()

# Wait for the thread to complete
thread.join()

print("Thread execution completed.")

Output

1
2
3
4
5
Thread execution completed.

Using Thread Subclass

You can also create a thread by subclassing the Thread class and overriding the run method.

Example

import threading

class NumberPrinter(threading.Thread):
    def __init__(self, n):
        super().__init__()
        self.n = n

    def run(self):
        for i in range(1, self.n + 1):
            print(i)

# Create a thread
thread = NumberPrinter(5)

# Start the thread
thread.start()

# Wait for the thread to complete
thread.join()

print("Thread execution completed.")

Output

1
2
3
4
5
Thread execution completed.

Synchronizing Threads

To avoid race conditions and ensure data integrity, you can use synchronization mechanisms like Locks, Events, Conditions, Semaphores, and Barriers.

Using Locks

A Lock ensures that only one thread can access a shared resource at a time.

Example

import threading

class SharedCounter:
    def __init__(self):
        self.counter = 0
        self.lock = threading.Lock()

    def increment(self):
        with self.lock:
            self.counter += 1
            print(f"Counter: {self.counter}")

counter = SharedCounter()

def worker():
    for _ in range(5):
        counter.increment()

# Create threads
threads = [threading.Thread(target=worker) for _ in range(3)]

# Start threads
for thread in threads:
    thread.start()

# Wait for all threads to complete
for thread in threads:
    thread.join()

print("All threads have finished execution.")

Output

Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 5
Counter: 6
Counter: 7
Counter: 8
Counter: 9
Counter: 10
Counter: 11
Counter: 12
Counter: 13
Counter: 14
Counter: 15
All threads have finished execution.

Using Events

An Event allows one thread to signal one or more other threads that some condition has occurred.

Example

import threading
import time

event = threading.Event()

def waiter():
    print("Waiting for event to be set.")
    event.wait()
    print("Event has been set!")

def setter():
    time.sleep(2)
    print("Setting the event.")
    event.set()

# Create threads
thread1 = threading.Thread(target=waiter)
thread2 = threading.Thread(target=setter)

# Start threads
thread1.start()
thread2.start()

# Wait for all threads to complete
thread1.join()
thread2.join()

print("All threads have finished execution.")

Output

Waiting for event to be set.
Setting the event.
Event has been set!
All threads have finished execution.

Using Condition

A Condition allows one or more threads to wait until they are notified by another thread.

Example

import threading

condition = threading.Condition()
items = []

def consumer():
    with condition:
        while not items:
            print("Consumer is waiting.")
            condition.wait()
        print("Consumer consumed item:", items.pop())

def producer():
    with condition:
        item = "item"
        items.append(item)
        print("Producer produced item:", item)
        condition.notify_all()

# Create threads
consumer_thread = threading.Thread(target=consumer)
producer_thread = threading.Thread(target=producer)

# Start threads
consumer_thread.start()
producer_thread.start()

# Wait for all threads to complete
consumer_thread.join()
producer_thread.join()

print("All threads have finished execution.")

Output

Consumer is waiting.
Producer produced item: item
Consumer consumed item: item
All threads have finished execution.

Python threading Module Functions

The threading module in Python provides a way to create and manage threads, allowing for concurrent execution of code. This can be useful for improving the performance of programs that perform multiple tasks simultaneously. Below is a list of some commonly used functions and classes in the threading module, along with their descriptions and links to detailed guides for each function.

For a complete tutorial, visit Python threading Module Tutorial.

 Table

Function/Class Description
threading.Thread A class for creating and managing individual threads.
threading.Lock A class for creating a lock object, used to ensure that only one thread accesses a resource at a time.
threading.RLock A reentrant lock that allows a thread to acquire the same lock multiple times.
threading.Condition A class for condition variables, which allow threads to wait for some condition to be met.
threading.Event A class for event objects, which allow threads to wait for an event to be set.
threading.Semaphore A class for semaphore objects, used to control access to a resource by a set number of threads.
threading.BoundedSemaphore A bounded semaphore that prevents the semaphore’s value from going above a specified maximum.
threading.Timer A class for creating a timer that runs a function after a specified interval.
threading.Barrier A class for creating a barrier that blocks threads until a specified number of threads have reached it.
threading.local A class for creating thread-local data, which is data that is unique to each thread.
threading.active_count() Returns the number of currently active threads.
threading.current_thread() Returns the current thread object.
threading.enumerate() Returns a list of all currently active thread objects.
threading.main_thread() Returns the main thread object.
threading.get_ident() Returns the current thread’s identifier.
threading.get_native_id() Returns the native integral thread ID of the current thread.
threading.settrace() Sets a trace function for all threads started from the threading module.
threading.setprofile() Sets a profile function for all threads started from the threading module.
threading.stack_size() Returns the size of the thread stack used when creating new threads.

Conclusion

Multithreading in Python allows you to run multiple threads concurrently, making your programs more efficient and responsive. The threading module provides various classes and methods to create, manage, and synchronize threads. Understanding and using these concepts effectively can help you build robust and efficient multithreaded applications.

Leave a Comment

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

Scroll to Top