直接回答:tokio 的调度器用少量工作线程(默认等于核数)协作式运行成千上万个任务,前提是每个任务的 poll 都很快返回。一旦某个任务在 poll 里执行阻塞 IO(同步文件读、数据库驱动)或长耗时计算(图像处理、加密),它占住的工作线程就无法调度其他任务——整个 runtime 的延迟全面恶化,吞吐塌方。spawn_blocking 把这类代码挪到一个独立的、容量大得多的阻塞线程池(默认上限 512),与异步工作线程隔离。
展开解析:判断标准社区经验是“超过几十微秒的非 await 连续计算就该考虑挪走”,阻塞 IO 一律挪走。注意区分两个相似的 API:spawn_blocking 适合“一次性、不确定何时结束”的阻塞调用,线程退出后归还;真正的长 CPU 密集流水线更适合自建专用 rayon 池,通过 channel 与 tokio 侧通信,避免阻塞池被计算任务占满而影响偶发的同步调用。反过来 spawn 一个 async 任务包裹阻塞代码没有用——阻塞发生在 poll 内部,照样卡线程。观测手段:tokio-console 可以看到任务的 poll 时长分布与 worker 繁忙度;线上现象常表现为 P99 延迟毛刺与吞吐不成比例地掉。面试加分:提及 async 的“染色”问题——同步库一旦侵入异步代码很难回头,选型时优先原生异步驱动。
追问方向:block_in_place 与 spawn_blocking 有何区别?阻塞池打满会怎样?
(约 480 字)