rust
42 lines · 7 steps
Graceful thread shutdown with an atomic flag
A worker pool signals its threads to stop by flipping one shared atomic boolean, then joins them cleanly.
Explained by
highlit
1use std::sync::atomic::{AtomicBool, Ordering};
2use std::sync::Arc;
3use std::thread;
4use std::time::Duration;
5
6struct WorkerPool {
7 shutdown: Arc<AtomicBool>,
8 handles: Vec<thread::JoinHandle<()>>,
9}
10
11impl WorkerPool {
12 fn new(workers: usize) -> Self {
13 let shutdown = Arc::new(AtomicBool::new(false));
14 let mut handles = Vec::with_capacity(workers);
15
16 for id in 0..workers {
17 let flag = Arc::clone(&shutdown);
18 handles.push(thread::spawn(move || {
19 while !flag.load(Ordering::Acquire) {
20 process_next_task(id);
21 thread::sleep(Duration::from_millis(50));
22 }
23 eprintln!("worker {id} draining and exiting");
24 }));
25 }
26
27 WorkerPool { shutdown, handles }
28 }
29
30 fn shutdown(self) {
31 self.shutdown.store(true, Ordering::Release);
32 for handle in self.handles {
33 let _ = handle.join();
34 }
35 }
36}
37
38impl Drop for WorkerPool {
39 fn drop(&mut self) {
40 self.shutdown.store(true, Ordering::Release);
41 }
42}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A single shared AtomicBool is enough to coordinate cooperative shutdown across many threads without locks.
- 2Acquire/Release ordering pairs the writer's signal with each reader's check so the flag flip is reliably observed.
- 3Implementing Drop as a safety net ensures workers stop even when explicit shutdown is skipped.
Related explainers
rust
use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(untagged)]
Parsing flexible JSON shapes with serde
deserialization
enums
json
Intermediate
6 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
rust
use chrono::{Duration, NaiveDate}; #[derive(Debug)] pub struct DateRange {
Parsing and iterating date ranges in Rust
error-handling
iterators
parsing
Intermediate
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/graceful-thread-shutdown-with-an-atomic-flag-explained-rust-0685/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.