rust async 中 5 种致命阻塞写法
下面这 5 种阻塞写法,在 Tokio / async 环境里 不是“慢”,而是“致命” ——
线上事故 80% 都是它们之一。
❌ 1️⃣ 在 async 中使用 std::thread::sleep
❌ 错误写法
async fn handler() { std::thread::sleep(std::time::Duration::from_secs(5)); }
💥 后果
-
阻塞 Tokio worker 线程
-
该线程上的 所有 Future 停止执行
-
WebSocket 心跳、HTTP 超时、定时器全部卡死
📌 不是当前 task 睡 5 秒,而是整个线程睡 5 秒
✅ 正确写法
use tokio::time::{sleep, Duration}; async fn handler() { sleep(Duration::from_secs(5)).await; }
🧠 口诀
async 里:只 sleep Future,不 sleep 线程
❌ 2️⃣ 在 async 中调用阻塞 I/O(文件 / 网络 / stdin)
❌ 错误写法
async fn read_file() { let data = std::fs::read_to_string("big.txt").unwrap(); }
💥 后果
-
文件 I/O 是 阻塞系统调用
-
runtime worker 被占死
-
高并发下吞吐直接崩
✅ 正确写法(两种)
✔ async 版本
use tokio::fs; let data = fs::read_to_string("big.txt").await?;
✔ blocking 版本
let data = tokio::task::spawn_blocking(|| { std::fs::read_to_string("big.txt") }).await??;
🧠 口诀
看到 std::fs / std::net,就要警觉
❌ 3️⃣ 在 async 中使用 Mutex::lock()(std::sync)
❌ 错误写法
use std::sync::Mutex; async fn handler(m: Mutex<i32>) { let mut guard = m.lock().unwrap(); // 阻塞! *guard += 1; }
💥 后果
-
如果锁被占:
-
线程阻塞
-
不是 task 阻塞
-
-
死锁极其隐蔽
-
Web 服务会“随机卡死”
✅ 正确写法
use tokio::sync::Mutex; async fn handler(m: Mutex<i32>) { let mut guard = m.lock().await; *guard += 1; }
🧠 口诀
async 里只用 async Mutex
❌ 4️⃣ 在 async 中执行 CPU 重任务(for/while/加密/压缩)
❌ 错误写法
async fn heavy() { let mut sum = 0; for i in 0..1_000_000_000 { sum += i; } }
💥 后果
-
Future 一直占着 worker 线程
-
没有
.await→ 无法被抢占 -
runtime 实际退化成单线程
✅ 正确写法
let result = tokio::task::spawn_blocking(|| {
heavy_compute()
}).await?;
🧠 口诀
async 不是并行,CPU 任务必须丢出去
❌ 5️⃣ 在 async 中等待同步 Join / recv / wait
❌ 错误写法
等线程
let h = std::thread::spawn(|| heavy()); h.join().unwrap(); // 阻塞
等 channel
rx.recv().unwrap(); // std::sync::mpsc
💥 后果
-
worker 线程被阻塞
-
Tokio 调度器失效
-
常见于“半 async 半 sync”项目
✅ 正确写法
rx.recv().await; // tokio::sync::mpsc
或
let h = tokio::spawn(task());
h.await?;
🧠 口诀
async 里只等 Future,不等同步结果
🔥 总结:致命阻塞速查表
| 错误行为 | 替代方案 |
|---|---|
| thread::sleep | tokio::time::sleep |
| std::fs | tokio::fs / spawn_blocking |
| std::sync::Mutex | tokio::sync::Mutex |
| CPU 重循环 | spawn_blocking |
| join / recv | await |
🧠 终极判断公式
如果这一行代码:
没有
.await可能等待外部事件
或消耗大量 CPU
👉 99% 是阻塞 bug
🚨 工程级建议
async fn 里禁止 std::
runtime 线程 = 不能等
await 是唯一合法“等待”
重活永远 spawn_blocking
卡顿 ≈ 阻塞 bug
浙公网安备 33010602011771号