A critical region is code that accesses shared state and must not run concurrently with another execution context. A lock, or mutex, lets one process, thread, or coroutine acquire exclusive access, while others wait until the lock is released. section 6.5.2 section 11.2
Use critical regions to keep shared operations consistent:
- Acquire a lock before reading or changing shared state, perform the critical work, and release the lock afterward. section 6.5.2
- Protect a shared counter in multiprocessing or threading so concurrent increments do not produce a race condition. section 6.5.2 section 7.3
- Use async with for asyncio locks; blocked coroutines suspend while other asynchronous work continues. section 11.2
Two coroutines sharing one lock
Two coroutines attempt to enter the same critical region. The first coroutine acquires the lock and works; the second waits, then enters only after the first releases it. A similar pattern protects a socket lookup while a user disconnects. section 11.2 section 11.0
Listing 11.4 Using an asyncio lock
import asyncio
from asyncio import Lock
from util import delay
async def a(lock: Lock):
print('Coroutine a waiting to acquire the lock')
async with lock:
print('Coroutine a is in the critical section')
await delay(2)
print('Coroutine a released the lock')
async def b(lock: Lock):
print('Coroutine b waiting to acquire the lock')
async with lock:
print('Coroutine b is in the critical section')
await delay(2)
print('Coroutine b released the lock')
async def main():
lock = Lock()
await asyncio.gather(a(lock), b(lock))
asyncio.run(main())
Runnable asyncio example showing shared-lock acquisition, critical-region execution, waiting, and release. section 11.2








