Rust
Summary - Concurrency
Combine thread, Arc and Mutex to build a parallel counter.
Concurrency summary
thread::spawn starts threads with owning (move) closures; thread::scope allows borrowing. For sharing use Arc, and for mutation put a Mutex (single access) or RwLock (many readers/one writer) inside. Thanks to the Send and Sync marker traits, data races are prevented at compile time; a deadlock, however, is a logic bug and is prevented by lock ordering.
Parallel word count
Each text is counted on a separate thread, and the result is added to the shared map in a single step under the lock:
let sayim = Arc::new(Mutex::new(HashMap::new()));
thread::scope(|s| {
for metin in &metinler {
let sayim = Arc::clone(&sayim);
s.spawn(move || {
let mut yerel: HashMap<String, usize> = HashMap::new();
for k in metin.split_whitespace() {
*yerel.entry(k.to_string()).or_insert(0) += 1;
}
let mut ortak = sayim.lock().unwrap();
for (k, n) in yerel {
*ortak.entry(k).or_insert(0) += n;
}
});
}
});
Exercise Parallel count
Count the texts ["elma armut", "elma", "armut kiraz elma"] on separate threads as above. Collect the result into a vector, sort and print it with {:?}.
Show example solution
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;
fn main() {
let metinler = ["elma armut", "elma", "armut kiraz elma"];
let sayim = Arc::new(Mutex::new(HashMap::new()));
thread::scope(|s| {
for metin in &metinler {
let sayim = Arc::clone(&sayim);
s.spawn(move || {
let mut yerel: HashMap<String, usize> = HashMap::new();
for k in metin.split_whitespace() {
*yerel.entry(k.to_string()).or_insert(0) += 1;
}
let mut ortak = sayim.lock().unwrap();
for (k, n) in yerel {
*ortak.entry(k).or_insert(0) += n;
}
});
}
});
let mut v: Vec<(String, usize)> = sayim.lock().unwrap().clone().into_iter().collect();
v.sort();
println!("{:?}", v);
}
Test yourself
-
In a parallel word count, why is it faster for each thread to fill its own local HashMap and merge at the end?
- It uses less memory
- There's no difference
- Waiting on a shared lock for every word disappears
- HashMap isn't thread safe
Show answer
Waiting on a shared lock for every word disappears — Lock contention kills parallelism; minimizing shared state is the main rule.
-
Shared map
let sayim = Arc::new(____::new(HashMap::new()));
Show answer
let sayim = Arc::new(Mutex::new(HashMap::new()));
-
Group summary
- thread::scope
- Borrowing threads
- Mutex
- Single access
- RwLock
- Many readers
- Send/Sync
- Compile-time safety
Next lesson: mpsc channel