Thread pool in Rust
Rust's standard library has std::thread but
no built-in thread pool. Common approaches: the
rayon crate for CPU-bound parallelism, the
threadpool crate for a classic job queue, or a manual pool
with mpsc channels. See
What is Thread Pool?.
1. rayon — global thread pool (data parallelism)
Most idiomatic for CPU-bound work. Rayon maintains a work-stealing pool and provides parallel iterators — no manual queue needed.
use rayon::prelude::*;
fn main() {
let nums: Vec<i32> = (0..10).collect();
// rayon splits work across its internal thread pool
let sum: i32 = nums.par_iter().map(|x| x * 2).sum();
println!("sum = {}", sum);
nums.par_iter().for_each(|x| {
println!("task {} on {:?}", x, std::thread::current().id());
});
}
2. threadpool crate — classic job pool
Submit closures to a fixed-size pool; closest to Java
ExecutorService.
use threadpool::ThreadPool;
fn main() {
let pool = ThreadPool::new(4);
for i in 0..10 {
pool.execute(move || {
println!("task {} on {:?}", i, std::thread::current().id());
});
}
pool.join(); // wait for all tasks
}
3. Manual pool — std::thread + mpsc
Build your own with worker threads and a channel — no external crate beyond std.
use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel::<Box<dyn FnOnce() + Send>>();
let rx = std::sync::Arc::new(std::sync::Mutex::new(rx));
let num_workers = 4;
for _ in 0..num_workers {
let rx = std::sync::Arc::clone(&rx);
thread::spawn(move || loop {
let job = rx.lock().unwrap().recv();
match job {
Ok(f) => f(),
Err(_) => break,
}
});
}
for i in 0..10 {
tx.send(Box::new(move || println!("task {}", i))).unwrap();
}
drop(tx); // close channel when done submitting
thread::sleep(std::time::Duration::from_secs(1));
}
4. Tokio — blocking thread pool (async runtime)
For async code, CPU-heavy or blocking work should run on Tokio's
blocking pool via spawn_blocking — not on async tasks.
use tokio::task;
#[tokio::main]
async fn main() {
let handle = task::spawn_blocking(|| {
// runs on tokio's blocking thread pool
std::thread::sleep(std::time::Duration::from_millis(100));
42
});
let result = handle.await.unwrap();
println!("result = {}", result);
}
Summary: Use rayon for parallel loops,
threadpool for explicit submit/join, or Tokio's blocking
pool inside async applications.