直接回答:事件循环是单线程的协作式调度器:协程运行到 await 一个未完成的 Future 时挂起、交还控制权,I/O 就绪后由循环回调恢复执行。因为所有协程共享一个线程,任何一个协程执行阻塞调用(time.sleep、requests、同步 DB 驱动)都会冻结整个循环——其他协程全部饿死,这与 tokio 里阻塞 worker 线程是同一性质的事故。
展开解析:正确姿势:阻塞 I/O 用 asyncio.to_thread(3.9+)丢到线程池;CPU 密集任务用进程池(GIL 下线程帮不了计算);库选型认准 async 生态(aiohttp、asyncpg)。常见坑:async def 只是定义协程,不 await/不 create_task 就永不执行——"coroutine was never awaited"警告要当真;asyncio.gather 默认一个异常取消其余不等待结果(return_exceptions=True 改变语义);任务要持引用(create_task 后存入集合),否则可能被 GC 提前回收(3.11 的 TaskGroup 从结构上解决)。调试手段:asyncio debug 模式报告慢回调;loop.slow_callback_duration 设阈值。最后明确收益边界:asyncio 提升的是 I/O 并发密度(单进程万级连接),对延迟本身没有魔法。
追问方向:await 与 yield from 的历史关系?TaskGroup 相比 gather 的结构化并发改进?协程与生成器的实现关联?
(约 450 字)