Waitgroups in Rust
Our example demonstrates how to wait for multiple threads to finish using a WaitGroup
. In Rust, we’ll use the std::sync::Arc
and std::sync::Barrier
to achieve similar functionality.
use std::sync::{Arc, Barrier};
use std::thread;
use std::time::Duration;
// This is the function we'll run in every thread.
fn worker(id: i32) {
println!("Worker {} starting", id);
// Sleep to simulate an expensive task.
thread::sleep(Duration::from_secs(1));
println!("Worker {} done", id);
}
fn main() {
// This Barrier is used to wait for all the
// threads launched here to finish.
let barrier = Arc::new(Barrier::new(5));
// Launch several threads and clone the Barrier
// for each.
let mut handles = vec![];
for i in 1..=5 {
let b = Arc::clone(&barrier);
// Spawn a new thread and store its handle
let handle = thread::spawn(move || {
worker(i);
// Wait for all threads to reach this point
b.wait();
});
handles.push(handle);
}
// Wait for all threads to complete
for handle in handles {
handle.join().unwrap();
}
// Note that this approach has no straightforward way
// to propagate errors from workers. For more
// advanced use cases, consider using channels or
// other synchronization primitives.
}
To run the program:
$ cargo run
Worker 5 starting
Worker 1 starting
Worker 4 starting
Worker 3 starting
Worker 2 starting
Worker 4 done
Worker 1 done
Worker 2 done
Worker 5 done
Worker 3 done
The order of workers starting up and finishing is likely to be different for each invocation.
In this Rust version:
We use
Arc<Barrier>
instead ofWaitGroup
.Arc
provides thread-safe reference counting, allowing us to share theBarrier
across multiple threads.The
Barrier
is created with a count of 5, matching the number of threads we’ll spawn.We spawn threads using
thread::spawn
and store their handles in a vector.Each thread calls
worker()
and then waits at the barrier usingb.wait()
.In the main thread, we wait for all spawned threads to complete by calling
join()
on each handle.
This approach ensures all threads complete before the program exits, similar to the WaitGroup
in the original example. However, Rust’s ownership and borrowing rules provide additional safety guarantees at compile time.