Skip to content

AsyncIO: Single-Threaded Concurrency

AsyncIO is modern Python’s answer to the challenge of building high-performance network applications. While threading and multiprocessing rely on the operating system to switch between tasks, AsyncIO uses Cooperative Multitasking.

In an AsyncIO program, a single thread runs an “Event Loop” that switches between tasks only when a task is waiting for I/O (like a database query or a web request).


A Coroutine is a specialized generator that can pause and resume.

  • async def: Defines a coroutine. Calling it returns a coroutine object, but doesn’t run the code.
  • await: Pauses the current coroutine and yields control back to the event loop until the result is ready.
simple_async.py
import asyncio
async def fetch_api():
print("Requesting data...")
await asyncio.sleep(1) # Non-blocking wait
print("Data received!")
return {"status": 200}
# Run the coroutine
result = asyncio.run(fetch_api())

Think of the Event Loop as a traffic controller. It keeps a list of all active coroutines.

  1. It runs Coroutine A until it hits an await.
  2. If Coroutine A is waiting for a network response, the loop parks it and switches to Coroutine B.
  3. As soon as the network response for A arrives, the loop resumes A where it left off.

If you just await one thing after another, your code is still sequential. To run things at the same time, you must create Tasks.

concurrent_tasks.py
async def main():
# Schedule two tasks to run concurrently
task1 = asyncio.create_task(fetch_api())
task2 = asyncio.create_task(fetch_api())
# Wait for both to finish
val1 = await task1
val2 = await task2


Why use AsyncIO if it only uses one core? Efficiency.

  1. Low RAM: A thread consumes about 8MB of RAM for its stack. A coroutine consumes only about 2KB. You can run 100,000 coroutines on a laptop, but you can’t run 100,000 threads.
  2. No Race Conditions: Since everything runs in one thread, you don’t need Locks for most simple variables. (Though you still need to be careful with shared resources across await points).

FeatureSync CodeAsyncIO
LogicTop to bottom.Cooperative (yields control).
I/O HandlingStops everything until done.Switches tasks while waiting.
Keyworddefasync def
Wait Commandresult = call()result = await call()