Python Lock
last modified February 15, 2025
In this article we show how to synchronize Python threads using threading.Lock
.
Lock
is a synchronization primitive that ensures only one thread
can access a shared resource at a time. It is useful for preventing race
conditions when multiple threads attempt to modify shared data simultaneously.
A Lock
has two states: locked and unlocked. A thread can acquire a
lock using the acquire
method and release it using the
release
method. If the lock is already acquired by another thread,
the calling thread will block until the lock is released.
Lock Example
The following example demonstrates how to use threading.Lock
to
protect a shared resource.
import threading class SharedResource: def __init__(self): self.lock = threading.Lock() self.value = 0 def increment(self): with self.lock: # Acquire and release the lock automatically self.value += 1 print(f"Value after increment: {self.value}") def worker(shared_resource): for _ in range(5): # Each thread increments the value 5 times shared_resource.increment() def main(): shared_resource = SharedResource() threads = [] for i in range(3): # Create 3 threads thread = threading.Thread(target=worker, args=(shared_resource,)) threads.append(thread) thread.start() for thread in threads: thread.join() # Wait for all threads to complete print(f"Final value: {shared_resource.value}") if __name__ == "__main__": main()
In this program, a Lock
is used to protect a shared integer value.
Multiple threads increment the value, and the lock ensures that only one thread
can modify the value at a time.
self.lock = threading.Lock()
The SharedResource
class is initialized with a Lock
to
protect the shared value.
with self.lock: # Acquire and release the lock automatically
The with
statement is used to automatically acquire and release the
lock, ensuring proper synchronization.
shared_resource = SharedResource()
The SharedResource
is initialized, and the lock is used to protect
the shared value.
$ python main.py Value after increment: 1 Value after increment: 2 Value after increment: 3 Value after increment: 4 Value after increment: 5 Value after increment: 6 Value after increment: 7 Value after increment: 8 Value after increment: 9 Value after increment: 10 Value after increment: 11 Value after increment: 12 Value after increment: 13 Value after increment: 14 Value after increment: 15 Final value: 15
Lock with Timeout
The following example demonstrates how to use a Lock
with a
timeout. If a thread cannot acquire the lock within the specified timeout, it
will proceed without modifying the shared resource.
import threading import time class SharedResource: def __init__(self): self.lock = threading.Lock() self.value = 0 def increment(self): if self.lock.acquire(timeout=1): # Try to acquire the lock with a timeout try: self.value += 1 print(f"Value after increment: {self.value}") finally: self.lock.release() # Release the lock else: print("Could not acquire the lock") def worker(shared_resource): for _ in range(5): # Each thread increments the value 5 times shared_resource.increment() time.sleep(0.5) # Simulate some work def main(): shared_resource = SharedResource() threads = [] for i in range(3): # Create 3 threads thread = threading.Thread(target=worker, args=(shared_resource,)) threads.append(thread) thread.start() for thread in threads: thread.join() # Wait for all threads to complete print(f"Final value: {shared_resource.value}") if __name__ == "__main__": main()
In this program, the Lock
is used with a timeout. If a thread
cannot acquire the lock within 1 second, it will proceed without modifying the
shared resource.
if self.lock.acquire(timeout=1): # Try to acquire the lock with a timeout
The acquire
method is called with a timeout of 1 second. If the
lock is not acquired within this time, the thread proceeds without modifying the
shared resource.
$ python main.py Value after increment: 1 Value after increment: 2 Value after increment: 3 Value after increment: 4 Value after increment: 5 Value after increment: 6 Value after increment: 7 Value after increment: 8 Value after increment: 9 Value after increment: 10 Value after increment: 11 Value after increment: 12 Value after increment: 13 Value after increment: 14 Value after increment: 15 Final value: 15
Thread-Safe Queue with Lock Example
The following example demonstrates how to use a threading.Lock
to
implement a thread-safe queue. The lock ensures that only one thread can modify
the queue at a time.
import threading import time import queue class ThreadSafeQueue: def __init__(self): self.queue = queue.Queue() self.lock = threading.Lock() def put(self, item): with self.lock: # Acquire and release the lock automatically self.queue.put(item) print(f"Added item: {item}") def get(self): with self.lock: # Acquire and release the lock automatically if not self.queue.empty(): item = self.queue.get() print(f"Removed item: {item}") return item return None def producer(safe_queue): for i in range(5): # Produce 5 items safe_queue.put(i) time.sleep(0.5) # Simulate production time def consumer(safe_queue): for _ in range(5): # Consume 5 items safe_queue.get() time.sleep(1) # Simulate consumption time def main(): safe_queue = ThreadSafeQueue() # Create producer and consumer threads producer_thread = threading.Thread(target=producer, args=(safe_queue,)) consumer_thread = threading.Thread(target=consumer, args=(safe_queue,)) producer_thread.start() consumer_thread.start() producer_thread.join() consumer_thread.join() print("Queue operations completed") if __name__ == "__main__": main()
In this program, a Lock
is used to protect a shared queue. The
producer thread adds items to the queue, and the consumer thread removes items
from the queue. The lock ensures that only one thread can modify the queue at a
time.
self.lock = threading.Lock()
The ThreadSafeQueue
class is initialized with a Lock
to protect the shared queue.
with self.lock: # Acquire and release the lock automatically
The with
statement is used to automatically acquire and release the
lock, ensuring proper synchronization.
safe_queue = ThreadSafeQueue()
The ThreadSafeQueue
is initialized, and the lock is used to protect
the shared queue.
$ python main.py Added item: 0 Removed item: 0 Added item: 1 Added item: 2 Removed item: 1 Added item: 3 Added item: 4 Removed item: 2 Removed item: 3 Removed item: 4 Queue operations completed
Source
In this article we have shown how to synchronize Python threads using Lock for resource management.
Author
List all Python tutorials.