How to Use threading.RLock in Python
Demonstrates threading.RLock, a reentrant lock that allows the same thread to acquire it multiple times without deadlocking — essential for recursive functions sharing state across threads.
Python code
24 linesimport threading
import time
lock = threading.RLock()
shared_counter = 0
def recursive_increment(value, depth):
global shared_counter
with lock:
shared_counter += 1
print(f"Depth {depth}: counter = {shared_counter}")
if depth > 1:
recursive_increment(value, depth - 1)
def worker(value):
recursive_increment(value, 3)
if __name__ == "__main__":
threads = [threading.Thread(target=worker, args=(i,)) for i in range(2)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"Final counter: {shared_counter}")
Output
Depth 3: counter = 1
Depth 2: counter = 2
Depth 1: counter = 3
Depth 3: counter = 4
Depth 2: counter = 5
Depth 1: counter = 6
Final counter: 6
How it works
threading.RLock (reentrant lock) tracks ownership per thread and a recursion count, so the same thread can acquire it multiple times safely. This is crucial for recursive functions, since a plain threading.Lock would deadlock the moment the function tries to acquire it again. The with lock: statement ensures every acquire is matched by a release, even when exceptions propagate. Each thread runs recursive_increment to depth 3, and the counter increments in a thread-safe way thanks to the lock. The final value reflects the total number of increments across all threads.
Common mistakes
- Using a plain Lock instead of RLock, which causes a deadlock in recursive code
- Forgetting to join threads before reading the final counter, leading to a race condition
- Acquiring the RLock without a `with` block and failing to release it on early returns
Variations
- Use `lock.acquire()` / `lock.release()` explicitly for finer control in non-blocking scenarios
- Replace recursion with an iterative loop and a single `with lock:` block to avoid needing RLock at all
Real-world use cases
- Guarding shared state inside a recursive tree or graph traversal that runs across multiple threads.
- Protecting a nested lock acquisition pattern, like a cache layer calling a locked database function.
- Updating a shared counter or accumulator from recursive worker threads in a parallel processing pipeline.
Sponsored
More from Concurrency & performance
- Benchmark list.append vs deque.append in Python medium
- Build a Python Performance Profiler That Generates Readable Reports medium
- Graceful Shutdown Executor Context Manager in Python medium
- How to Build a Producer-Consumer Pattern with asyncio.Queue in Python medium
- How to Cancel an asyncio Task with Graceful Cleanup in Python medium
- How to Convert Data in Parallel with ThreadPoolExecutor in Python easy
Keep learning
Related tutorials and quizzes for this topic.