trio.run() 不能在已有 async 上下文中调用,因为它会启动新事件循环并接管线程,嵌套调用将触发 runtimeerror;正确做法是统一以 trio.run() 启动主入口,跨框架交互需用 to_thread/from_thread 桥接。

trio.run() 为什么不能在已有 async 上下文里调用
因为 trio.run() 本身会启动一个全新的事件循环,并接管当前线程的控制权;如果它被嵌套在另一个 async 函数(比如 asyncio 的 await 块)中,就会触发 RuntimeError: trio.run() must be called from a synchronous context。
常见错误现象:在 Jupyter Notebook 里直接写 await trio.sleep(1) 报错,或试图在 FastAPI 的 async def endpoint() 里调用 trio.run()。
- 正确做法是把整个并发逻辑包进
trio.run(),而不是反过来 - 若需和 asyncio 交互,用
trio.to_thread.run_sync()或trio.from_thread.run_sync()跨桥,别硬塞trio.run() - Jupyter 中可借助
trio.lowlevel.current_task()判断是否已在 trio 环境中,但更稳妥的是统一用trio.run()启动主入口
spawn_system_task 和 start_soon 的区别在哪
start_soon() 启动的是“普通任务”,受父作用域(如 nursery)生命周期约束;spawn_system_task() 启动的是脱离 nursery 管理的后台任务,常用于监控、日志轮转、心跳上报等长生命周期行为。
容易踩的坑:误用 spawn_system_task() 去跑本该随请求结束的任务,导致资源泄漏或状态错乱。
立即学习“Python免费学习笔记(深入)”;
无论从何种情形出发,在目前校长负责制的制度安排下,中小学校长作为学校的领导者、管理者和教育者,其管理水平对于学校发展的重要性都是不言而喻的。从这个角度看,建立科学的校长绩效评价体系以及拥有相对应的评估手段和工具,有利于教育行政机关针对校长的管理实践全过程及其结果进行测定与衡量,做出价值判断和评估,从而有利于强化学校教学管理,提升教学质量,并衍生带来校长转变管理观念,提升自身综合管理素质。
-
start_soon()必须在nursery内调用,且其子任务会在nursery退出时被自动取消并等待完成 -
spawn_system_task()可在任意位置调用,不绑定 nursery,也不会被自动清理——你得自己管它的生命周期 - 性能影响:系统任务绕过 nursery 的结构化约束,调试难度上升;除非真需要“永远运行”,否则优先用
start_soon()
如何安全地在 trio 里调用阻塞 IO(比如 requests.get)
直接调用 requests.get() 会阻塞整个 trio 事件循环,让所有其他任务卡住。trio 不提供类似 asyncio 的 loop.run_in_executor() 语法糖,必须显式走线程桥接。
正确路径只有一条:用 trio.to_thread.run_sync() 包裹阻塞调用,并确保传入的函数是纯同步的。
- 别写
await requests.get(...)——requests没有 async 接口 - 别在
run_sync()里传async def函数,它只接受同步 callable - 若高频调用,考虑换用
httpx.AsyncClient,它原生支持 trio,避免线程切换开销 - 注意:线程池默认大小是
trio.to_thread.current_default_thread_limiter().max_value,高并发时可能成为瓶颈,必要时手动调大
CancelScope 的 timeout 参数为 0 会发生什么
timeout=0 不代表“不限时”,而是立刻触发取消——进入该 CancelScope 的代码块将几乎立即收到 trio.Cancelled 异常,哪怕里面只有一行 print("hi")。
这个值常被误认为“跳过超时逻辑”,实际是“强制立即取消”的快捷写法,适合做快速失败兜底。
-
with trio.move_on_after(0):等价于立刻抛出Cancelled,可用于模拟不可达分支或短路判断 - 若想表达“无超时”,应显式省略
timeout参数,或传None(部分 API 支持) - 注意嵌套
CancelScope时,外层timeout=0会让内层根本没机会执行——这不是 bug,是结构化取消的设计前提
结构化并发的复杂点不在语法,而在取消传播的隐式路径。哪怕只多一层 nursery 或一个 move_on_after,都可能改变异常流向和资源释放时机。写的时候盯着 nursery 边界和 cancel scope 嵌套层级,比记住函数名重要得多。









