While Rust provides tokio::join!
and std::sync::Barrier
for synchronizing tasks, WaitGroup
(available in crossbeam
and tokio
) offers a lightweight, efficient way to wait for multiple tasks to complete, similar to Go’s sync.WaitGroup
.
Using tokio::sync::Notify
to implement a simple WaitGroup
:
use std::sync::Arc;
use tokio::sync::Notify;
use tokio::task;
struct WaitGroup {
count: Arc<tokio::sync::Notify>,
}
impl WaitGroup {
fn new() -> Self {
Self {
count: Arc::new(Notify::new()),
}
}
async fn wait(&self) {
self.count.notified().await;
}
fn done(&self) {
self.count.notify_waiters();
}
}
#[tokio::main]
async fn main() {
let wg = WaitGroup::new();
let wg_clone = wg.clone();
let handle = task::spawn(async move {
// Simulate async work
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
wg_clone.done();
});
wg.wait().await;
println!("All tasks completed!");
handle.await.unwrap();
}
- Efficient Task Synchronization: Prevents polling overhead compared to spinning on an atomic counter.
- Lightweight Alternative to
Barrier
: UnlikeBarrier
, it doesn’t require knowing task count upfront. - Ideal for Dynamic Workloads: Easily synchronizes a varying number of concurrent async tasks.
- Parallel Data Processing: Wait for multiple database queries or I/O operations.
- Async Task Pools: Manage concurrent task execution without manual counters.
- Server Shutdown Handling: Ensure graceful shutdown when background workers finish.
Challenge: Extend this implementation to support reference counting for dynamically spawning and tracking worker tasks. How would you handle cancellations gracefully?