Python – Thread Synchronization

Introduction

Thread synchronization is essential in multithreaded programming to prevent race conditions and ensure data integrity when multiple threads access shared resources. Python provides several synchronization primitives in the threading module, such as Locks, RLocks, Semaphores, Conditions, and Events.

Synchronization Primitives

1. Lock

A Lock is the simplest form of synchronization primitive. It ensures that only one thread can access a resource at a time.

Example

import threading lock = threading.Lock() shared_resource = 0 def increment(): global shared_resource for _ in range(1000000): with lock: shared_resource += 1 # Create threads threads = [threading.Thread(target=increment) for _ in range(2)] # Start threads for thread in threads: thread.start() # Wait for all threads to complete for thread in threads: thread.join() print(f"Final value of shared_resource: {shared_resource}") 

Output

Final value of shared_resource: 2000000 

2. RLock (Reentrant Lock)

An RLock (Reentrant Lock) allows a thread to acquire the same lock multiple times without causing a deadlock.

Example

import threading rlock = threading.RLock() shared_resource = 0 def increment(): global shared_resource for _ in range(1000000): with rlock: shared_resource += 1 # Create threads threads = [threading.Thread(target=increment) for _ in range(2)] # Start threads for thread in threads: thread.start() # Wait for all threads to complete for thread in threads: thread.join() print(f"Final value of shared_resource: {shared_resource}") 

Output

Final value of shared_resource: 2000000 

3. Semaphore

A Semaphore controls access to a resource with a fixed number of permits.

Example

import threading import time semaphore = threading.Semaphore(2) def access_resource(thread_id): with semaphore: print(f"Thread {thread_id} accessing resource") time.sleep(2) print(f"Thread {thread_id} releasing resource") # Create threads threads = [threading.Thread(target=access_resource, args=(i,)) for i in range(4)] # 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

Thread 0 accessing resource Thread 1 accessing resource Thread 0 releasing resource Thread 1 releasing resource Thread 2 accessing resource Thread 3 accessing resource Thread 2 releasing resource Thread 3 releasing resource All threads have finished execution. 

4. Condition

A Condition allows threads to wait for certain conditions to be met before continuing execution. It is often used in conjunction with a Lock or RLock.

Example

import threading condition = threading.Condition() queue = [] def consumer(): with condition: condition.wait() # Wait for the condition to be notified while queue: item = queue.pop(0) print(f"Consumed: {item}") def producer(): with condition: for i in range(5): queue.append(i) print(f"Produced: {i}") condition.notify_all() # Notify all waiting threads # 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

Produced: 0 Produced: 1 Produced: 2 Produced: 3 Produced: 4 Consumed: 0 Consumed: 1 Consumed: 2 Consumed: 3 Consumed: 4 All threads have finished execution. 

5. Event

An Event is a simple synchronization primitive that allows one thread to signal one or more other threads that an event has occurred.

Example

import threading import time event = threading.Event() def waiter(): print("Waiting for event to be set") event.wait() # Wait for the event to be set print("Event has been set!") def setter(): time.sleep(2) print("Setting the event") event.set() # Create threads waiter_thread = threading.Thread(target=waiter) setter_thread = threading.Thread(target=setter) # Start threads waiter_thread.start() setter_thread.start() # Wait for all threads to complete waiter_thread.join() setter_thread.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. 

Conclusion

Thread synchronization is crucial for managing concurrent access to shared resources and preventing race conditions in multithreaded programs. Python’s threading module provides several synchronization primitives such as Locks, RLocks, Semaphores, Conditions, and Events to help you manage thread synchronization effectively. Understanding these primitives and how to use them will enable you to write robust and efficient multithreaded applications in Python.

Leave a Comment

Scroll to Top