bigywater.tech
Rust · 104

阻塞代码混入异步任务

异步代码里调用阻塞函数会怎样?影响有多大?阻塞多少个任务才会出问题?怎么修?这篇在一个 4 线程的 tokio 运行时里,把阻塞任务数从 0 增加到 6,量出延迟的跳变点,再验证 spawn_blocking 与 block_in_place。

3 对 260 ms
3 个与 4 个阻塞任务时,主任务 sleep(10 ms) 的延迟
3 ms
同样的阻塞工作改用 spawn_blocking 后的延迟
panic
block_in_place 用在 current_thread 运行时的结果

阻塞任务数与延迟

实测 · 本机执行输出
    0 个阻塞任务 / 4 个工作线程:主任务 sleep(10 ms) 实际晚醒了约     6 ms
    1 个阻塞任务 / 4 个工作线程:主任务 sleep(10 ms) 实际晚醒了约     5 ms
    2 个阻塞任务 / 4 个工作线程:主任务 sleep(10 ms) 实际晚醒了约     5 ms
    3 个阻塞任务 / 4 个工作线程:主任务 sleep(10 ms) 实际晚醒了约     3 ms
    4 个阻塞任务 / 4 个工作线程:主任务 sleep(10 ms) 实际晚醒了约   260 ms
    6 个阻塞任务 / 4 个工作线程:主任务 sleep(10 ms) 实际晚醒了约   269 ms

tokio 的调度见Tokio 任务调度;手写执行器中的同类现象见Async Runtime 做了什么。

spawn_blocking

实测 · 本机执行输出
    6 个 spawn_blocking 任务同时在跑:主任务 sleep(10 ms) 晚醒了约 3 ms

block_in_place

实测 · 本机执行输出
    2 个 block_in_place 任务各阻塞 300 ms(运行时只配了 2 个工作线程):主任务 sleep(10 ms) 晚醒了约 5 ms
  
  
  thread 'main' (7226321) panicked at src/main.rs:2:19:
  can call blocking only when running on the multi-threaded runtime
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

CPU 密集的计算

实测 · 本机执行输出
    4 个 200 ms 的纯计算任务占满 4 个工作线程:主任务的 sleep(10 ms) 实际用了约 178 ms

并行计算见Rayon 并行迭代。

分步演示

阻塞代码混入异步
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。tokio 1.x;release 构建,每组只测一次(延迟有几毫秒波动)
    • 没有验证:阻塞线程池的默认上限与线程回收、block_in_place 的内部实现(是否补充工作线程)、async-std 的行为、阻塞 I/O(文件、DNS)在 tokio 里的默认处理
    京ICP备2026067502号-1京公网安备11010502063487号