How to Use asyncio Lock to Protect a Shared Counter in Python
This code demonstrates how to use an asyncio.Lock to safely increment a shared counter from multiple concurrent coroutines.
Python code
19 linesimport asyncio
async def increment(counter, lock, increments):
for _ in range(increments):
async with lock:
counter[0] += 1
async def main():
counter = [0]
lock = asyncio.Lock()
tasks = [
increment(counter, lock, 1000)
for _ in range(5)
]
await asyncio.gather(*tasks)
print(f"Final counter value: {counter[0]}")
if __name__ == "__main__":
asyncio.run(main())
Output
Final counter value: 5000
How it works
The asyncio.Lock is an async context manager that ensures only one coroutine can enter the critical section at a time. By wrapping the counter[0] += 1 operation inside async with lock, we prevent race conditions that would cause lost updates. The await asyncio.gather(*tasks) runs all five increment tasks concurrently, and with the lock, the counter correctly reaches 5000. Without the lock, the counter would likely be less due to interleaving.
Common mistakes
- Forgetting to use `await` when acquiring the lock via `async with lock`.
- Using a regular `threading.Lock` instead of `asyncio.Lock` in async code.
- Not using a mutable container (like a list) when modifying a variable from async closures.
Variations
- Use `asyncio.Semaphore` for limiting concurrent access instead of mutual exclusion.
- Use `asyncio.Queue` with a single worker to eliminate the need for a lock in some designs.
Real-world use cases
- Updating a shared in-memory metrics counter from multiple concurrent API request handlers.
- Accumulating results from parallel I/O operations into a common data structure.
- Maintaining a shared rate limiter state across concurrent tasks in a web service.
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.