Code of the Day
AdvancedConcurrency & Performance

asyncio

Single-threaded concurrency for I/O — handle thousands of waits at once.

PythonAdvanced9 min read
By the end of this lesson you will be able to:
  • Write async functions with async/await
  • Run tasks concurrently with asyncio.gather
  • Explain when asyncio beats threads

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 ; 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 loop

await 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 3s

The 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.

Finished reading? Mark it complete to track your progress.

On this page