asyncio
Single-threaded concurrency for I/O — handle thousands of waits at once.
- Write async functions with async/await
- Run tasks concurrently with asyncio.gather
- Explain when asyncio beats threads
asyncio brings the event-loop model — the same idea as JavaScript's runtime — to
Python. It's single-threaded concurrency: one thread juggling thousands of
waiting operations by switching between them whenever one would block. For
high-volume I/O, it scales further than threads.
async and await
An async def function is a coroutine; await pauses it while waiting,
letting the event loop run other coroutines in the meantime:
import asyncio
async def fetch(name):
print(f"start {name}")
await asyncio.sleep(1) # non-blocking wait; others run during it
print(f"done {name}")
return name
asyncio.run(fetch("a")) # asyncio.run drives the event loopawait only yields on genuinely awaitable operations (async I/O, asyncio.sleep)
— it doesn't help with CPU-bound work, which still blocks the single thread.
Running things concurrently
Awaiting one coroutine after another is sequential. asyncio.gather runs many
concurrently and waits for all — the parallelism idea from the concurrency
lesson, on one thread:
async def main():
results = await asyncio.gather(fetch("a"), fetch("b"), fetch("c"))
return results
asyncio.run(main()) # all three "downloads" overlap; ~1s total, not 3sThe three sleeps overlap because while one awaits, the loop runs the others.
asyncio vs threads
Both suit I/O-bound work; they differ in scale and style:
- Threads: work with existing blocking libraries; the OS switches them; a few hundred is fine.
- asyncio: needs async-aware libraries, but one thread can manage tens of thousands of concurrent waits with very little overhead — ideal for servers handling many simultaneous connections.
For CPU-bound work, neither helps — reach for processes (the GIL lesson).
Don't call blocking functions inside async code — a blocking call freezes the entire event loop, stalling every other task. Use async libraries throughout, or offload blocking work to a thread/process pool.
Where to go next
Concurrency is one route to speed; the other is making the code itself faster. Next: profiling and optimization.