选 asyncio 还是 trio 取决于团队是否愿为结构化并发“多写两行”:asyncio 适合已有稳定生态的项目,trio 更利于新项目与可维护性,anyio 是渐进迁移兼容层,curio 仅适用于教学或原理验证。

选 asyncio 还是 Trio,关键看团队是否愿意为结构化并发“多写两行”
如果你的团队已经用 asyncio 写了一年爬虫、API 网关或微服务,且没出过严重取消泄漏或异常吞没问题,asyncio 仍是合理选择——它不是“过时”,而是“够用但需自律”。Trio 的 trio.open_nursery() 强制你显式管理子任务生命周期,比如忘记 await nursery.aclose() 或漏掉 async with,代码根本跑不起来;而 asyncio.create_task() 不报错,只默默泄漏任务。这不是 Trio 更“高级”,是它把“谁负责 cleanup”这个隐含契约,变成了语法强制。
- 新项目、小团队、重视可维护性 → 直接上 Trio,省去后期重构成本
- 已有大量
asyncio+aiohttp/aiomysql代码 → 先别动,除非出现Task was destroyed but it is pending!这类警告 - 用
asyncio.run()启动但内部混用loop.create_task()和asyncio.ensure_future()→ 已埋雷,Trio 的 nursery 模型能帮你提前暴露
AnyIO 是“兼容层”,不是“替代品”,别指望它自动修复 asyncio 的设计债
AnyIO 的核心价值是统一 API:同一段 anyio.sleep()、anyio.open_tcp_stream() 可以在 asyncio 或 trio 调度器下运行。但它不改变底层行为——比如你在 anyio 下用 asyncio 后端,依然会遇到 asyncio.CancelledError 不向上冒泡、contextvars 在 run_in_executor 中丢失的问题。它适合渐进迁移,比如先改 HTTP 客户端为 httpx(原生支持 AnyIO),再逐步替换数据库层。
- 想平滑过渡现有
asyncio服务 → 用anyio.run_sync_in_worker_thread()替代loop.run_in_executor(),避免线程上下文污染 - 依赖
asyncio.Queue或自定义asyncio.Event→ AnyIO 的create_queue()和Event行为一致,但参数名可能不同(如max_sizevsmax_items) - 误以为加了
anyio就自动获得 Trio 的取消传播 → 不会,后端仍是asyncio,该吞异常还是吞
Curio 几乎不用考虑,除非你在写教学演示或嵌入式协程调度器
Curio 是 David Beazley 的实验性作品,极简、无事件循环抽象、纯协程驱动。它没有 asyncio 的兼容包袱,也没有 Trio 的结构化语义,更不像 AnyIO 那样做适配。PyPI 数据显示,2025 年前 100 的异步库中仅 3% 声明支持 Curio,主流 HTTP 客户端(httpx、aiohttp)和 ORM(orm、piccolo)均不支持。它的存在意义是证明“协程可以更轻”,不是提供生产方案。
- 用于理解协程调度原理 → 写个 50 行的
curio.sleep()+curio.run()demo 很清晰 - 已有
asyncio生态(FastAPI、Starlette)→ Curio 无法直接集成,得重写所有中间件 - 看到 “Curio 更快” 的 benchmark → 注意它常测的是纯内存调度,真实 I/O 场景下差异被网络延迟掩盖
真正卡住团队的不是框架选型,而是上下文变量和取消信号的传播方式
三个框架对 contextvars 和取消异常的处理差异,比语法糖更影响长期稳定性。比如 asyncio 中,asyncio.current_task().cancel() 不会自动终止其子任务;Trio 的 CancelScope 则天然级联;AnyIO 在 trio 后端下继承该行为,在 asyncio 后端下则受限于底层。同样,asyncio 的 contextvars.ContextVar 在 run_in_executor() 中默认丢失,Trio 的 trio.to_thread.run_sync() 默认保留,AnyIO 的 run_sync_in_worker_thread() 也保留——但前提是你的函数没手动清空上下文。
立即学习“Python免费学习笔记(深入)”;
- 日志 trace_id、用户 auth token 等需要跨协程传递 → 优先选 Trio 或 AnyIO(trio 后端),避免自己手写
copy_context() - 调用阻塞库(如旧版
requests、numpy.linalg.svd)→ Trio/AnyIO 的线程封装更安全,asyncio需额外处理loop.set_exception_handler() - 用
asyncio.shield()保护关键清理逻辑 → Trio 用with CancelScope(shield=True),语义等价但更直观
框架选型最易被忽略的一点:调试体验。asyncio 的 traceback 常止步于 await 行,Trio 的错误栈会完整展示 nursery 层级,AnyIO 则取决于后端。如果团队里有人还不会看 trio._core._run.CF 这种内部类型,先别急着切 Trio——工具链的成熟度,有时比框架本身的设计更重要。










