CodeBlanks Logo Icon CodeBlanks

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;
            }
        });
    }
});

Practice 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 {:?}.

Starter code

main.rs
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    // buraya yaz
}

Sample solution

main.rs
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);
}

Expected output

[("armut", 2), ("elma", 3), ("kiraz", 1)]

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 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 Correct answer
  • HashMap isn't thread safe

Explanation: Lock contention kills parallelism; minimizing shared state is the main rule.

2 Shared map

Question
let sayim = Arc::new(____::new(HashMap::new()));
Answer
let sayim = Arc::new(Mutex::new(HashMap::new()));

Explanation: Arc<Mutex<T>> for mutable sharing across threads.

3 Group summary

Term Meaning
thread::scope Borrowing threads
Mutex Single access
RwLock Many readers
Send/Sync Compile-time safety

All lessons in Advanced