liveBook · Manning

Reentrant lock

As explained in Python Concurrency with asyncio

A reentrant lock is a synchronization lock that its owning thread can acquire multiple times, requiring a matching release for each acquisition.

A reentrant lock lets the same thread acquire a lock repeatedly, including while recursively re-entering a critical section. Each acquisition requires a matching release; an internal recursion count releases the lock for other threads only when it reaches zero. Python provides this behavior through threading.RLock. section 7.3.1

Recursive list summation

A recursive sum_list function acquires a normal Lock, processes one list element, and calls itself for the remainder. The second acquisition blocks the same thread forever. Replacing Lock with RLock allows the recursion to continue, provided every acquisition is eventually released. section 7.3.1

Use reentrant locks to address self-reentry, but keep their limits in mind:

  • They prevent a thread from deadlocking itself when recursion or nested method calls reacquire the same lock. section 7.3.1
  • They do not resolve deadlocks between threads that hold different locks and wait for one another. section 7.3.2
  • They require balanced acquisition and release so the recursion count can return to zero. section 7.3.1
Listing 7.9 Recursion with locks
from threading import Lock, Thread
from typing import List
 
list_lock = Lock()
 
 
def sum_list(int_list: List[int]) -> int:
    print('Waiting to acquire lock...')
    with list_lock:
        print('Acquired lock.')
        if len(int_list) == 0:
            print('Finished summing.')
            return 0
        else:
            head, *tail = int_list
            print('Summing rest of list.')
            return head + sum_list(tail)
 
 
thread = Thread(target=sum_list, args=([1, 2, 3, 4],))
thread.start()
thread.join()

Trace the recursive lock acquisition that hangs with Lock before comparing it with the RLock replacement. section 7.3.1

How Python Concurrency with asyncio covers it

How deep it goes
Explains recursive acquisition, matching release, and internal counting, but defers implementation details beyond the recursion count.
Examples
Demonstrates recursive list summation and a thread-safe integer list with nested locked method calls.
What it assumes
Assumes basic locks and threads, while developing the distinction between self-deadlock and multi-thread deadlock.
What you can do after
Diagnose/debug with it by recognizing self-deadlock and replacing Lock with threading.RLock when same-thread reentry is required.
How it connects
Connects reentrant locks with threads, recursion, critical sections, race conditions, and deadlocks.

Also explained in

Compare how 2 books explain reentrant lock →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Python Concurrency with asyncio. The section links point to the parts of the book it draws on.