# **Reentrant Locks**
A **Reentrant Lock** (or `RLock`) is a synchronization primitive in Python that allows a thread to acquire the same lock multiple times without causing a deadlock. It’s particularly useful for recursive functions or nested critical sections.
---
## **Why Use Reentrant Locks?**
Unlike a standard lock (`threading.Lock`), a `Reentrant Lock` tracks ownership of the lock and keeps a count of how many times it has been acquired by the owning thread. This ensures the thread can safely re-enter critical sections without blocking itself.
---
## **Basic Example**
Here’s how a `Reentrant Lock` works:
```python
import threading
lock = threading.RLock()
def task():
with lock:
print("First level acquired")
with lock:
print("Second level acquired")
thread = threading.Thread(target=task)
thread.start()
thread.join()
```
Output:
```
First level acquired
Second level acquired
```
---
## **Use Case: Recursive Functions**
Reentrant locks are ideal for recursive functions that need to enter the same critical section multiple times.
```python
import threading
lock = threading.RLock()
def recursive_function(n):
with lock:
if n > 0:
print(f"Recursive level {n}")
recursive_function(n - 1)
thread = threading.Thread(target=lambda: recursive_function(3))
thread.start()
thread.join()
```
---
## **Avoiding Deadlocks in Nested Locks**
Reentrant locks simplify scenarios with nested locks, preventing deadlocks caused by the same thread re-acquiring a lock.
```python
import threading
lock = threading.RLock()
def task():
with lock:
print("Outer lock acquired")
with lock:
print("Inner lock acquired")
thread = threading.Thread(target=task)
thread.start()
thread.join()
```
---
## **Limitations of Reentrant Locks**
- **Thread-specific**: A reentrant lock only allows the same thread to re-acquire it. Other threads must wait for the lock to be released.
- **Not for I/O-bound tasks**: Reentrant locks are not optimized for high concurrency situations with many threads.
---
## **Best Practices**
1. Use `threading.RLock` for recursive or nested locking scenarios.
2. Avoid unnecessarily deep recursion or excessive nested locks to minimize complexity and potential performance issues.
3. Ensure locks are always released using context managers (`with` statement) or `try-finally`.
---
## **Explore Next**
- [[Synchronization Mechanisms]]: Overview of locking strategies.
- [[Data Race Prevention]]: Techniques to manage shared resources safely.
- [[Deadlocks]]: Understanding and preventing deadlock scenarios.
---
This note provides an in-depth explanation of reentrant locks and is formatted for direct use in Obsidian.