Coordinating Asyncio Tasks with Locks and Events

asyncio sync primitives are traffic signals for coroutines, preventing collisions over shared state. Use a Lock for exclusive access or an Event to signal multiple tasks to proceed. Footgun: these are for asyncio tasks only, not OS threads.
Why it exists
In concurrent programming, multiple tasks might try to read or write the same piece of memory at the same time, leading to corrupted data, known as a race condition. Asyncio needs a way to coordinate these tasks to ensure safe access to shared resources within its single-threaded event loop.
The mental model
Think of synchronization primitives as rules of communication for your coroutines. A Lock is a "talking stick": only the coroutine holding the lock can access the shared resource. An Event is a starting pistol: one coroutine fires it, and all coroutines waiting for it start running at once. A Condition is more complex, like waiting for a specific announcement (the condition) before you can grab the talking stick (the lock).
How it works
These primitives manage internal state to control task execution. A Lock has a 'locked' or 'unlocked' state. A coroutine calls 'await lock.acquire()' and pauses if the lock is held by another task. 'lock.release()' allows a waiting task to proceed. An Event has a flag, initially false. Tasks 'await event.wait()' and pause. When another task calls 'event.set()', the waiting tasks are awakened. The preferred way to use them is with an 'async with' block, which handles the acquire and release logic automatically and safely.
When to use it
Use a Lock when you need to ensure only one task can modify a shared variable or resource at a time, like updating a counter or writing to a shared dictionary. Use an Event when one task needs to signal a state change to multiple other tasks, such as "initialization is complete, you can all start working now." Use a Condition for complex producer-consumer scenarios where tasks must wait for a specific state change while holding a lock.
When not to use it
The biggest mistake is using asyncio primitives for synchronizing traditional OS threads. They are not thread-safe and will fail unpredictably. For multi-threaded code, you must use the 'threading' module's equivalents (e.g., 'threading.Lock'). Also, do not use the 'timeout' argument as you would in 'threading'; instead, wrap the operation with 'asyncio.wait_for()'.
One canonical example
To prevent multiple tasks from writing to a shared list simultaneously, you'd use a Lock. For example: shared_list = [], lock = asyncio.Lock(). An async function would then use async with lock: before appending to the list. Without the lock, another task could interleave its own operation during an await, leading to incorrect ordering or data corruption if the logic were more complex.
Interview question
Which statement accurately describes a key limitation or misuse of asyncio synchronization primitives?
- a.They cannot be used with the async with statement for automatic resource management.
- b.They are not suitable for protecting shared resources accessed by traditional OS threads.Correct
- c.They are primarily designed for coordinating tasks across different Python interpreters.
- d.They are only effective for preventing race conditions in CPU-bound operations.
Why? this is the answer
The card explicitly states that asyncio primitives are not thread-safe and should not be used for synchronizing traditional OS threads, as they will fail unpredictably. Option A is incorrect because the card recommends using 'async with' for safe and automatic management of these primitives.
Just read this? Test yourself on what you have been reading.
Read the original → docs.python.org
You just looked this up. Could you explain it out loud?
That is the part interviews actually test. Tezvyn takes questions like this one and gives you what the interviewer is really checking, the answer that lands, and the mistake that ends the conversation, in the four minutes before your next meeting.
The iPhone app is on the way
We are building it. Until it lands, nothing here is held back from you: every interview card, your saved cards, streaks and the job board all work in Safari, plus hundreds of free practice quizzes of thirty questions each. Sign in and it all carries over to the app the day it arrives.
Want it as an icon? Tap Share at the bottom of Safari, then Add to Home Screen. It opens full screen and the cards you have read stay available offline.
We are hiring for this. Open roles that interview on python — each one lists the topics its interview covers.
See open roles