Petter Holt Juliussen • Mail | GitHub | Letterboxd

for later reference.

Asynchronous I/O (asyncio)



Async IO is a bit lesser known than its tried-and-true cousins, multiprocessing and threading:

Concurrency encompasses both multiprocessing (ideal for CPU-bound tasks) and threading (suited for IO-bound tasks). Multiprocessing is a form of parallelism, with parallelism being a specific type (subset) of concurrency. The Python standard library has offered longstanding support for both of these through its multiprocessing, threading, and concurrent.futures packages.

The asyncio module (introduced in Python 3.4) provides infrastructure for writing single-threaded concurrent code using coroutines, multiplexing I/O access over sockets and other resources, running network clients and servers, and other related primitives.

Async IO is not threading, nor is it multiprocessing. It is not built on top of either of these. Async IO is a single-threaded, single-process design: it uses cooperative multitasking. Coroutines (a central feature of async IO) can be scheduled concurrently, but they are not inherently concurrent.

Warning: Python’s async IO API has evolved rapidly from Python 3.4 to Python 3.7. Some old patterns are no longer used, and some things that were at first disallowed are now allowed through new introductions.

Event loop

The event loop is the central execution device provided by asyncio.


Coroutines declared with async/await syntax is specialized generator functions and the preferred way of writing asyncio applications. A coroutine is a function that can suspend its execution before reaching return, and it can indirectly pass control to another coroutine for some time.


import asyncio

async def count():
    await asyncio.sleep(1)

async def main():
    await asyncio.gather(count(), count(), count())

if __name__ == "__main__":
    import time
    s = time.perf_counter()
    elapsed = time.perf_counter() - s
    print(f"{__file__} executed in {elapsed:0.2f} seconds.")