rust
40 lines · 7 steps
A round-robin worker pool in Rust
Spawns a fixed set of worker threads, each with its own channel, and spreads tasks across them with an atomic counter.
Explained by
highlit
1use std::sync::atomic::{AtomicUsize, Ordering};
2use std::sync::Arc;
3use std::thread;
4
5pub struct RoundRobin<T> {
6 workers: Vec<crossbeam_channel::Sender<T>>,
7 next: AtomicUsize,
8}
9
10impl<T: Send + 'static> RoundRobin<T> {
11 pub fn new<F>(count: usize, handler: F) -> Arc<Self>
12 where
13 F: Fn(usize, T) + Send + Sync + Clone + 'static,
14 {
15 let mut workers = Vec::with_capacity(count);
16
17 for id in 0..count {
18 let (tx, rx) = crossbeam_channel::unbounded::<T>();
19 let handler = handler.clone();
20 thread::spawn(move || {
21 for task in rx {
22 handler(id, task);
23 }
24 });
25 workers.push(tx);
26 }
27
28 Arc::new(Self {
29 workers,
30 next: AtomicUsize::new(0),
31 })
32 }
33
34 pub fn dispatch(&self, task: T) {
35 let idx = self.next.fetch_add(1, Ordering::Relaxed) % self.workers.len();
36 if self.workers[idx].send(task).is_err() {
37 eprintln!("worker {idx} channel closed, dropping task");
38 }
39 }
40}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Giving each worker its own channel avoids contention on a single shared queue.
- 2An atomic counter with modulo arithmetic distributes work evenly without a lock.
- 3Handling a closed channel on send keeps the dispatcher from panicking when a worker dies.
Related explainers
rust
use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(untagged)]
Parsing flexible JSON shapes with serde
deserialization
enums
json
Intermediate
6 steps
go
package streaming import ( "bufio"
Streaming NDJSON logs over HTTP in Go
http-streaming
channels
select
Advanced
10 steps
rust
use std::cmp::{Ordering, Reverse}; use std::collections::BinaryHeap; use std::fs::File; use std::io::{self, BufRead, BufReader, BufWriter, Lines, Write};
K-way merge of sorted logs in Rust
binary-heap
k-way-merge
streaming-io
Intermediate
8 steps
rust
use axum::{ extract::{Path, State}, response::sse::{Event, KeepAlive, Sse}, };
Streaming import progress with SSE in Axum
server-sent-events
streams
watch-channel
Advanced
7 steps
rust
use std::f64::consts::PI; #[derive(Debug, Clone, Copy)] pub struct LatLng {
Building geographic bounding boxes in Rust
geospatial
value-types
option
Intermediate
7 steps
go
func (w *Watcher) resetDebounce(d time.Duration) { if !w.timer.Stop() { select { case <-w.timer.C:
Debouncing a stream of events in Go
debounce
timers
channels
Advanced
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/a-round-robin-worker-pool-in-rust-explained-rust-1f8b/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.