Appearance
Harness 工作该放 Event Loop、Thread 还是 Process:I/O 与 CPU 并发选择
前置:Harness 共享状态怎么保护:Lock、Coroutine、Thread 与 Process
核心问题:HTTP、阻塞 SDK 和 CPU-heavy 计算分别应该放 Event Loop、Thread 还是 Process,放错位置会怎样拖死 Harness?
async def 不会把 CPU 工作自动变快
Harness 的典型工作可以先分两类:
text
I/O-heavy
- LLM Provider HTTP
- MCP / REST Tool
- DB query
- object storage
CPU-heavy
- 大文本解析/压缩
- 本地 rerank/embedding(取决于实现)
- 图算法
- 图像/音频处理
- 本地模型推理I/O-heavy 的大量时间是在“等别人”,event loop 可以趁等待去推进其他 Run。CPU-heavy 则一直占执行核,直接放在 event loop 里会让所有 coroutine 的 timer、heartbeat、streaming 都延迟。
一个实用选择表
| Workload | 默认选择 | 原因 |
|---|---|---|
| async HTTP Model/Tool | coroutine | I/O 等待为主 |
| async DB driver | coroutine | I/O 等待为主 |
| 阻塞式第三方 SDK | thread | 避免卡 event loop |
| 小 JSON/schema validation | event loop 直接做 | 成本很短,切换不划算 |
| 数秒纯 Python CPU 计算 | process / 外部 worker | 避免 event loop starvation |
| GPU/本地模型 | 专用执行器/服务 | 有独立资源与并发约束 |
Thread 的核心价值常常是“把 blocking call 移出 event loop”,而不是保证 CPU-heavy Python 获得多核加速。
Harness 特别怕 Event Loop Starvation
如果一次 Context compaction 在 event loop 里连续占 CPU 4 秒,影响的不只是这个 Run:
text
heartbeat 延迟
→ lease 可能误判过期
SSE event 延迟
→ 用户以为卡死
cancellation 延迟
→ Tool 继续消耗资源
timeout callback 延迟
→ deadline 语义失真因此选择并发模型也是 Runtime correctness 问题,而不只是性能优化。
用 Profiling 决定,不凭函数名
不要看到 embedding() 就默认 CPU-heavy,也不要看到文件 I/O 就默认必须 thread。真正记录:
- wall-clock time;
- CPU time;
- event-loop lag;
- queue wait;
- thread/process serialization cost;
- downstream concurrency limit。
如果工作只有几百微秒,移到 process 的 IPC 成本反而更高。
用 Harness 任务判断并发模型
同一个 Agent Run 里可能同时出现三类工作:
text
等待模型 API / HTTP Tool → I/O-bound,asyncio 很合适
解析一个中等大小 JSON → 短 CPU 工作,可直接在 event loop 完成
本地对几十万行代码做重型 AST 分析 → CPU-bound,应考虑 thread/process/offload危险模式是把“用了 async”误解成“所有函数都不会阻塞”。如果 Tool 在 coroutine 里直接执行一个 3 秒 CPU 密集循环,那么这 3 秒内 event loop 不能及时处理其他 Run 的 heartbeat、cancel 或 stream event。
判断标准不是 API 名字,而是:这段工作的大部分时间是在等待外部 I/O,还是持续占用 CPU;它允许不允许阻塞 event loop;是否需要跨进程隔离。 Runtime 可以把这些特征放进 Tool metadata,由 Executor 选择 async、thread pool、process pool 或独立 worker。
如何验证
制造一个 2 秒纯 Python busy loop,并与 SSE heartbeat、timer 同时运行:
- 直接放 event loop:应观察到明显 loop lag;
- 移到 process/合适 executor:heartbeat 与 cancellation latency 应恢复。
自测
- HTTP Tool、压缩大文件、CPU 密集解析分别适合 Event Loop、Thread 还是 Process?你的判断依据是什么?
- 把 CPU 密集任务直接放进 Event Loop,会怎样反映在其他请求的 latency 上?