Skip to content
预计 1 分钟右侧目录可快速定位

asyncio 到底在调度什么:Coroutine、Event Loop 与 Awaitable ​

前置:ToolCall / ToolResult;基本 async / await

coro = fetch() 此时请求未必已发生;await coro 或 create task(coro) 后才开始推进。在 async Tool 里直接 time.sleep(5) 会让所有并发请求卡住;应使用异步 I/O 或移出线程/进程。

Mental Model ​

text
Coroutine → Task/TaskGroup → await/I-O suspension → resume/cancel/timeout → result/error

异步正确性首先看生命周期和失败传播,而不是“用了 async def 就更快”。每个 child task 都应有 owner、取消路径和资源上限。

Coroutine ​

Coroutine 是调用 async def 得到的可暂停执行对象;它只有被 await 或包装成 Task 后才会由 Event Loop 推进,单纯创建 coroutine object 不代表代码已经执行。

Event Loop ​

Event Loop 是 asyncio 的调度器:监视 I/O、timer 和 ready Task,在 coroutine 遇到 await 暂停时切换到其他可运行任务。它提供协作式并发,不等于多核并行。

任何长时间 blocking CPU/I/O 都会阻塞同一 loop 上所有 Agent task。

Awaitable ​

Awaitable 是可以出现在 await x 右侧的协议/对象类别,包含 coroutine、Task、Future 等。它表示“调用方可以等待其完成并取得结果”,并不等同于某一种具体调度实体。

最小实现 / 数据结构 ​

python
sem = asyncio.Semaphore(8)
async def run_one(call):
    async with sem:
        async with asyncio.timeout(call.timeout):
            return await call.execute()

在 Shadow Harness 中怎么落地 ​

ParallelToolExecutor 用 TaskGroup/Queue/Semaphore;Runtime policy 决定 partial failure,而不是盲从库默认。

Failure Modes ​

创建 Coroutine 但从未 await/调度 ​

症状: 调用 async function 得到 coroutine object 后直接丢弃或当结果使用。

为什么危险: 代码实际没执行,业务状态却可能继续。

处理: 明确 await/create task,并让 task 有 owner。

在 Event Loop 中执行 Blocking I/O/CPU 重任务 ​

症状: async def 内直接 requests/time.sleep 或长 CPU 计算。

为什么危险: 整个 loop 无法调度其他任务。

处理: I/O 用 async client;CPU/legacy blocking 放 thread/process executor 并限容量。

如何验证 ​

Case断言
创建 Coroutine 但从未 await/调度调用结束无未 await coroutine,预期副作用确实发生。
在 Event Loop 中执行 Blocking I/O/CPU 重任务并发短任务中插入 blocking call,优化后其他任务延迟不被整体拖住。

自测 ​

  1. 一个 coroutine 被创建但从未 await/schedule,它和真正运行中的 Task 有什么区别?Event Loop 实际调度的是什么?
  2. 在 async 函数里直接做长时间阻塞 I/O,会怎样影响同一个 Event Loop 上的其他 Tool Call?

Sources ​

AI Engineering · Agent · Harness · Backend Systems