Card 1 / 5
threads threads each increment one shared counter per times. Return the final total.
threads |
per |
result | why |
|---|---|---|---|
8 |
1000 |
8000 |
every increment lands, whatever the interleaving |
1 |
0 |
0 |
a thread that never increments |
0 |
100 |
0 |
no threads, no work |
The shape is Arc<Mutex<usize>>. Arc is a reference-counted pointer that lets several threads own the same allocation; Mutex makes each += 1 exclusive. Arc::clone(&counter) copies the pointer and bumps a refcount โ it does not copy the counter โ so every thread reaches the same one.
counter.lock() returns Result<MutexGuard<usize>, _> (a Result because a panic while holding the lock poisons it). Unwrap it, then *guard += 1 writes through the guard. The guard releases the lock when it drops, so *c.lock().unwrap() += 1; locks and unlocks within the one statement.
A Mutex is heavier than this job needs โ a shared AtomicUsize would do the same work lock-free. The Mutex is deliberate here: it is the tool this deck is teaching, and the Atomics deck revisits the same counter without it.
/// Spawn `threads` threads that each increment a shared counter `per` times.
/// Use Arc<Mutex<_>>. Return the final value.
fn shared_counter(threads: usize, per: usize) -> usize {
let counter = Arc::new(Mutex::new(0usize));
let handles: Vec<_> = (0..threads)
.map(|_| {
let c = Arc::clone(&counter);
thread::spawn(move || {
for _ in 0..per {
*c.lock().unwrap() += 1;
}
})
})
.collect();
for h in handles {
h.join().unwrap();
}
let value = *counter.lock().unwrap();
value
}Card 2 / 5
One thread per value in items, each pushing its value into a shared Vec. Return the collected values sorted ascending.