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

🚨 工程级建议

  1. async fn 里禁止 std::

  2. runtime 线程 = 不能等

  3. await 是唯一合法“等待”

  4. 重活永远 spawn_blocking

  5. 卡顿 ≈ 阻塞 bug

posted on 2026-01-05 17:26  myf008  阅读(3)  评论(0)    收藏  举报

导航