A single-threaded concurrency model uses one thread to interleave progress among multiple tasks. With asyncio, non-blocking I/O lets web requests, database queries, and network connections proceed in tandem without additional threads. section 2.0
Concurrency does not imply parallel CPU execution. CPU-bound coroutines still run sequentially on the event-loop thread, and can block unrelated tasks that use await. section 2.7.1
Use the model with these practical boundaries:
- Prefer it for long-running I/O operations that can yield control. section 2.0
- Do not expect multiple CPU-heavy tasks to finish faster merely because they are asyncio tasks; use multiprocessing with a process pool when CPU work must coexist with async code. section 2.7.1
- Treat shared state carefully: one executing Python line at a time avoids some races, but suspension at await can still produce inconsistent outcomes. section 11.1
- Use synchronization primitives when coroutines coordinate access, limit requests, protect resource pools, or wait for notifications. section 11.0
Two CPU-bound tasks
Creating two tasks for a large counting loop still runs the first loop and then the second, so total runtime is approximately the sum of both calls. A delay coroutine also waits because the CPU-bound work blocks the event loop. section 2.7.1
A single-threaded race
A counter increment without suspension completes safely in one thread, but reading shared state, awaiting, and writing it later allows another coroutine to intervene. An asyncio lock can serialize a message broadcast and user disconnection. section 11.1 section 11.2
Listing 2.18 Attempting to run CPU-bound code concurrently
import asyncio
from util import delay
@async_timed()
async def cpu_bound_work() -> int:
counter = 0
for i in range(100000000):
counter = counter + 1
return counter
@async_timed()
async def main():
task_one = asyncio.create_task(cpu_bound_work())
task_two = asyncio.create_task(cpu_bound_work())
await task_one
await task_two
asyncio.run(main())
Runnable asyncio code demonstrates that creating tasks does not make CPU-bound loops execute in parallel. section 2.7.1

