Python threading Condition Class

The threading.Condition class in Python's threading module provides a mechanism for threads to wait until they are notified. This is useful for scenarios where threads need to coordinate their actions and wait for certain conditions to be met before continuing execution.

Table of Contents

  1. Introduction
  2. threading.Condition Class Syntax
  3. Examples
    • Basic Usage
    • Producer-Consumer Example
    • Using Condition with Timeout
  4. Real-World Use Case
  5. Conclusion

Introduction

The threading.Condition class is used to manage conditions and provide a way for threads to wait for certain conditions to be met. It allows one or more threads to wait until they are notified by another thread that a condition has been met. This is particularly useful for implementing advanced thread synchronization patterns like producer-consumer or reader-writer.

threading.Condition Class Syntax

Here is how you create and use a condition with the threading.Condition class:

import threading

condition = threading.Condition()

Methods:

  • acquire(): Acquire the underlying lock.
  • release(): Release the underlying lock.
  • wait(timeout=None): Wait until notified or until a timeout occurs.
  • notify(n=1): Notify one or more threads that are waiting on this condition.
  • notify_all(): Notify all threads that are waiting on this condition.

Example

import threading

condition = threading.Condition()

def wait_for_condition():
    with condition:
        print(f"{threading.current_thread().name} waiting for condition")
        condition.wait()
        print(f"{threading.current_thread().name} condition met")

def notify_condition():
    with condition:
        print(f"{threading.current_thread().name} notifying condition")
        condition.notify()

# Create threads
wait_thread = threading.Thread(target=wait_for_condition, name="WaitThread")
notify_thread = threading.Thread(target=notify_condition, name="NotifyThread")

# Start threads
wait_thread.start()
notify_thread.start()

# Wait for threads to complete
wait_thread.join()
notify_thread.join()

Output:

WaitThread waiting for condition
NotifyThread notifying condition
WaitThread condition met

Examples

Basic Usage

Create and use a condition to synchronize threads.

Example

import threading

condition = threading.Condition()

def wait_for_condition():
    with condition:
        print(f"{threading.current_thread().name} waiting for condition")
        condition.wait()
        print(f"{threading.current_thread().name} condition met")

def notify_condition():
    with condition:
        print(f"{threading.current_thread().name} notifying condition")
        condition.notify()

# Create threads
wait_thread = threading.Thread(target=wait_for_condition, name="WaitThread")
notify_thread = threading.Thread(target=notify_condition, name="NotifyThread")

# Start threads
wait_thread.start()
notify_thread.start()

# Wait for threads to complete
wait_thread.join()
notify_thread.join()

Producer-Consumer Example

Implement a producer-consumer pattern using a condition.

Example

import threading
import time
import random

buffer = []
buffer_size = 10
condition = threading.Condition()

def producer():
    while True:
        item = random.randint(1, 100)
        with condition:
            while len(buffer) == buffer_size:
                condition.wait()
            buffer.append(item)
            print(f"Produced {item}")
            condition.notify()
        time.sleep(random.random())

def consumer():
    while True:
        with condition:
            while not buffer:
                condition.wait()
            item = buffer.pop(0)
            print(f"Consumed {item}")
            condition.notify()
        time.sleep(random.random())

producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

producer_thread.start()
consumer_thread.start()

producer_thread.join()
consumer_thread.join()

Using Condition with Timeout

Use a condition with a timeout to wait for a condition to be met.

Example

import threading

condition = threading.Condition()
data_ready = False

def wait_for_data():
    with condition:
        print(f"{threading.current_thread().name} waiting for data")
        result = condition.wait(timeout=5)
        if result:
            print(f"{threading.current_thread().name} data received")
        else:
            print(f"{threading.current_thread().name} timeout waiting for data")

def notify_data():
    global data_ready
    with condition:
        data_ready = True
        print(f"{threading.current_thread().name} notifying data")
        condition.notify()

# Create threads
wait_thread = threading.Thread(target=wait_for_data, name="WaitThread")
notify_thread = threading.Thread(target=notify_data, name="NotifyThread")

# Start threads
wait_thread.start()
time.sleep(1)  # Delay to ensure the wait thread starts waiting
notify_thread.start()

# Wait for threads to complete
wait_thread.join()
notify_thread.join()

Real-World Use Case

Thread-safe Queue

Implement a simple thread-safe queue using a condition.

Example

import threading
import time

class ThreadSafeQueue:
    def __init__(self, max_size):
        self.queue = []
        self.max_size = max_size
        self.condition = threading.Condition()

    def put(self, item):
        with self.condition:
            while len(self.queue) == self.max_size:
                self.condition.wait()
            self.queue.append(item)
            self.condition.notify()

    def get(self):
        with self.condition:
            while not self.queue:
                self.condition.wait()
            item = self.queue.pop(0)
            self.condition.notify()
            return item

queue = ThreadSafeQueue(5)

def producer():
    for i in range(10):
        queue.put(i)
        print(f"Produced {i}")
        time.sleep(1)

def consumer():
    for i in range(10):
        item = queue.get()
        print(f"Consumed {item}")
        time.sleep(2)

producer_thread = threading.Thread(target=producer)
consumer_thread = threading.Thread(target=consumer)

producer_thread.start()
consumer_thread.start()

producer_thread.join()
consumer_thread.join()

Conclusion

The threading.Condition class is used for managing complex synchronization scenarios in multithreaded programs. It allows threads to wait for certain conditions to be met before continuing execution, enabling the implementation of advanced thread synchronization patterns. Proper usage can significantly enhance the reliability and robustness of your concurrent applications.

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare