结论:I/O 密集、并发连接数高(成千上万)选 async——任务在用户态调度,每个任务只占几 KB 状态机,切换无系统调用;CPU 密集或并发数低的场景用 OS 线程(或 spawn_blocking)即可,代码更简单且能真正并行利用多核。
展开:async 的本质是协作式调度:.await 点让出执行权,运行时(tokio 等)用少量线程轮询大量 future。优势是内存和切换成本低一个数量级,10 万连接用线程需要 10 万 × 几 MB 栈,用 async 只是 10 万个状态机。代价:1)代码被"染色",async 函数只能被 async 调用;2).await 之间不能执行长时间阻塞操作,否则饿死同线程其他任务——阻塞调用要丢给 spawn_blocking;3)错误栈和调试体验更复杂。易错点:async 不等于并行,单线程运行时上两个 async 任务永不同时执行;跨 .await 持有的值必须 Send 才能被多线程运行时调度。
// 高并发 echo 服务:一行 tokio::spawn 一个轻量任务
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await?;
loop {
let (sock, _) = listener.accept().await?;
tokio::spawn(async move { handle(sock).await });
}
追问方向:Send bound 与 !Send 的局部任务(spawn_local)、协作式取消(drop future 即取消)的语义。