Mutexes in Rust

In the previous example we saw how to manage simple counter state using atomic operations. For more complex state we can use a mutex to safely access data across multiple threads.

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

// Container holds a map of counters; since we want to
// update it concurrently from multiple threads, we
// add a `Mutex` to synchronize access.
// Note that we're using `Arc` to allow sharing between threads.
struct Container {
    counters: Mutex<HashMap<String, i32>>,
}

impl Container {
    // The mutex is locked before accessing `counters`; it's automatically
    // unlocked when the MutexGuard goes out of scope at the end of the function.
    fn inc(&self, name: &str) {
        let mut counters = self.counters.lock().unwrap();
        *counters.entry(name.to_string()).or_insert(0) += 1;
    }
}

fn main() {
    // Note that the zero value of a mutex is usable as-is, so no
    // initialization is required here.
    let container = Arc::new(Container {
        counters: Mutex::new(HashMap::from([
            ("a".to_string(), 0),
            ("b".to_string(), 0),
        ])),
    });

    let mut handles = vec![];

    // This closure increments a named counter in a loop.
    let do_increment = |name: &str, n: i32, container: Arc<Container>| {
        for _ in 0..n {
            container.inc(name);
        }
    };

    // Run several threads concurrently; note
    // that they all access the same `Container`,
    // and two of them access the same counter.
    for _ in 0..2 {
        let container = Arc::clone(&container);
        handles.push(thread::spawn(move || {
            do_increment("a", 10000, container);
        }));
    }
    
    let container = Arc::clone(&container);
    handles.push(thread::spawn(move || {
        do_increment("b", 10000, container);
    }));

    // Wait for the threads to finish
    for handle in handles {
        handle.join().unwrap();
    }

    println!("{:?}", container.counters.lock().unwrap());
}

Running the program shows that the counters updated as expected.

$ cargo run
{a: 20000, b: 10000}

Next we’ll look at implementing this same state management task using only threads and channels.

This Rust code demonstrates the use of mutexes to safely manage shared state across multiple threads. It uses Arc (Atomic Reference Counting) to share the Container between threads, and Mutex to ensure that only one thread can access the counters at a time. The inc method locks the mutex before accessing the counters, ensuring thread-safe updates.

The main function creates several threads that concurrently increment counters, demonstrating how mutexes can be used to coordinate access to shared resources in a multi-threaded environment.