Theme
Q071. Why is blocking code dangerous inside async tasks?
Short Answer
Blocking code occupies an executor worker without yielding, so every other task assigned to that worker can be delayed. A few blocking calls or CPU-heavy loops can cause latency spikes and throughput collapse. Keep short non-blocking critical sections inline, move blocking I/O or bounded CPU work to spawn_blocking, and use a dedicated CPU pool such as Rayon for sustained computation.
Deep Dive
Async runtimes rely on cooperative scheduling. An ordinary blocking syscall, long lock wait, synchronous DNS call, or CPU loop does not return Pending, so the runtime cannot reuse that worker.
tokio::task::spawn_blocking runs closures on a separate blocking pool. Tokio may create many blocking threads up to a configured limit, so unbounded CPU jobs should also be limited with a semaphore or moved to a CPU-oriented pool. Once a spawn_blocking closure starts, aborting its JoinHandle does not stop it; cancellation must be built into the work.
block_in_place informs a multi-thread runtime that the current worker will block and lets Tokio hand off other tasks, but it still suspends other code in the same task and is unavailable on the current-thread runtime.
Internal Model
Executor workers poll task futures. A poll is expected to return quickly. Blocking prevents the worker from returning to its ready queue.
spawn_blocking enqueues a closure onto a separate thread pool and exposes its result through an async JoinHandle.
Example
rust
#[tokio::main]
async fn main() {
let sum = tokio::task::spawn_blocking(|| {
(1_u64..=1_000).sum::<u64>()
})
.await
.unwrap();
assert_eq!(sum, 500_500);
}Common Mistakes
- Calling a synchronous library from async code without checking whether it blocks.
- Sending unlimited CPU work to Tokio's blocking pool.
- Assuming
abortcan interrupt a runningspawn_blockingclosure.
Follow-ups
- When is a standard mutex acceptable inside async code?
- How should concurrency of blocking jobs be bounded?
- When should Rayon be used instead of
spawn_blocking?
Related
- Q043. What is the difference between concurrency and parallelism?
- Q047. How do
std::synclocks differ fromtokio::synclocks? - Q072. How does the Tokio runtime schedule tasks?
References
- Tokio Documentation
- Async Book