How to write concurrent Python with asyncio
Write concurrent Python with asyncio: define coroutines, run the event loop, await I/O concurrently with gather, manage tasks and timeouts, and choose asyncio, threads, or processes.
How asyncio works
asyncio is Python's framework for concurrent I/O-bound code. It runs many tasks on a single thread using cooperative multitasking: a coroutine voluntarily yields control with await while it waits for I/O, letting other coroutines run. This is ideal for network calls and not for CPU-bound work, because Python's global interpreter lock means CPU work does not parallelize across threads.
Prerequisites
- Python 3.10 or later
- Comfort with functions and return values
Steps
1. Write a coroutine
Define a coroutine with async def. Calling it returns a coroutine object, not a result.
import asyncio
async def fetch(name):
await asyncio.sleep(1) # simulates I/O
return f"done: {name}"
2. Run the event loop
asyncio.run starts the loop and runs a coroutine to completion.
result = asyncio.run(fetch("a"))
3. Await I/O concurrently with gather
Running awaits sequentially is slow. gather runs them concurrently and waits for all.
async def main():
results = await asyncio.gather(fetch("a"), fetch("b"), fetch("c"))
return results # all three finish in about 1 second
4. Create and manage tasks
create_task schedules a coroutine to run in the background so you can do other work before awaiting it.
task = asyncio.create_task(fetch("bg"))
# ... other work ...
await task
5. Add timeouts and cancellation
Guard against hangs with a timeout, which cancels the operation if it runs too long.
async with asyncio.timeout(2):
await slow_operation()
6. Choose asyncio, threads, or processes
Use asyncio for many concurrent I/O operations, threads for blocking libraries with no async support, and multiprocessing for CPU-bound work that must run in parallel.
Verification
Write a program that fetches three coroutines sequentially, then with gather, and time both. Confirm gather finishes in roughly the time of the slowest call, not the sum. Add a timeout to a deliberately slow coroutine and confirm it cancels.
Next Steps
Explore async context managers, asyncio.TaskGroup for structured concurrency, async HTTP clients, and running blocking code in an executor with run_in_executor.
Prerequisites
- Python 3.10+ installed
- Basic Python functions
Steps
- 1Write a coroutine
- 2Run the event loop
- 3Await I/O concurrently with gather
- 4Create and manage tasks
- 5Add timeouts and cancellation
- 6Choose asyncio, threads, or processes