135. Thread Synchronization with threading.Lock
Thread synchronization is essential when working with multithreading in Python, especially when threads need to access shared resources. The threading.Lock is a simple way to prevent race conditions and ensure that only one thread accesses a resource at a time.
1. Basic Example of Thread Synchronization with Lock
In this example, we create multiple threads that try to increment a shared counter. We use a Lock to prevent race conditions, ensuring the counter is incremented safely.
import threading
# Shared resource
counter = 0
# Create a lock object
counter_lock = threading.Lock()
# Function to increment the counter safely
def increment_counter():
global counter
with counter_lock: # Acquire the lock before modifying the shared resource
for _ in range(100000):
counter += 1 # Increment the shared counter
# Create threads
threads = []
for _ in range(5):
thread = threading.Thread(target=increment_counter)
threads.append(thread)
thread.start()
# Wait for all threads to complete
for thread in threads:
thread.join()
# Print the result after all threads have completed
print(f"Counter value after threads finish: {counter}")Explanation:
Shared Resource: The
countervariable is shared between multiple threads.Lock:
counter_lock = threading.Lock()creates a lock object that is used to synchronize access to the shared resource.Thread Function: Inside
increment_counter(), we use thewith counter_lock:statement to ensure that only one thread can increment the counter at a time. This prevents race conditions, where multiple threads could simultaneously modifycounterand cause inconsistent results.Thread Execution: Five threads are created, each calling the
increment_counter()function, and each thread safely increments thecounter.
Without the lock, the threads might overwrite each other's increments, leading to an incorrect final value. With the lock in place, the output will always be 500000, as the counter is incremented one step at a time.
2. Using Lock with Multiple Shared Resources
In more complex scenarios, you may need to synchronize access to multiple shared resources. Here's how you can do that with multiple locks:
Explanation:
Multiple Locks: In this case, we have two shared resources:
counter1andcounter2, each with its own lock (counter1_lockandcounter2_lock).Synchronization: We acquire the appropriate lock for each counter to ensure thread safety when incrementing both counters.
3. Deadlock Prevention in Locking
While using locks, it's important to avoid deadlocks, where two or more threads are waiting for each other to release a lock, leading to an infinite wait. A simple rule of thumb is to acquire locks in a consistent order to prevent deadlocks.
Explanation:
Lock Order: The locks are acquired in a consistent order:
lock1first, thenlock2. This avoids the possibility of deadlock by preventing circular wait conditions.
4. Using Lock for Critical Sections
In cases where only a small portion of code (a critical section) needs synchronization, using a Lock for just that section ensures minimal blocking.
Explanation:
Critical Section: Only the part of the function where the shared resource
shared_datais modified is locked. This minimizes the time the lock is held, improving performance in scenarios where only a small portion of code needs synchronization.
5. Locking in Producer-Consumer Model
In a more advanced scenario, you can use locks in a producer-consumer model, where one thread (the producer) adds items to a queue, and another thread (the consumer) processes them.
Explanation:
Producer: The producer adds items to the shared queue. Access to the queue is synchronized using a lock.
Consumer: The consumer processes items from the queue, ensuring safe access to the shared resource with a lock.
Conclusion:
Thread synchronization with threading.Lock is crucial to prevent race conditions when multiple threads access shared resources. Using locks ensures that only one thread can modify or access the resource at a time, ensuring data consistency and avoiding errors caused by concurrent access.
Last updated