Module 9 • Lesson 47

Shared State: Arc and Mutex

📚 10 min💻 Free🦀 nixus.pro

Shared State: Arc and Mutex

When you need shared mutable state across threads, use Arc (Atomic Reference Counting) for sharing and Mutex for safe mutation.

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

fn main() {
    // Mutex: mutual exclusion - only one thread at a time
    let counter = Arc::new(Mutex::new(0));
    let mut handles = vec![];

    for _ in 0..10 {
        let counter = Arc::clone(&counter); // Cheap: increments ref count
        let h = thread::spawn(move || {
            let mut num = counter.lock().unwrap(); // Acquire lock (blocks)
            *num += 1;
            // Lock released when `num` drops at end of block
        });
        handles.push(h);
    }

    for h in handles { h.join().unwrap(); }
    println!("Final count: {}", *counter.lock().unwrap()); // 10

    // RwLock: multiple readers OR one writer
    use std::sync::RwLock;
    let data = Arc::new(RwLock::new(vec![1, 2, 3]));
    let data_clone = Arc::clone(&data);
    thread::spawn(move || {
        let mut v = data_clone.write().unwrap(); // Exclusive write
        v.push(4);
    }).join().unwrap();

    let readers = vec![Arc::clone(&data), Arc::clone(&data)];
    for d in readers {
        thread::spawn(move || {
            let v = d.read().unwrap(); // Multiple reads allowed simultaneously
            println!("{:?}", *v);
        }).join().unwrap();
    }
}

Avoiding Deadlocks

use std::sync::{Arc, Mutex};

// Deadlock-safe pattern: always acquire locks in same order
fn transfer(from: &Mutex, to: &Mutex, amount: f64) {
    let mut f = from.lock().unwrap();
    let mut t = to.lock().unwrap();
    if *f >= amount { *f -= amount; *t += amount; }
}

// Minimize lock scope - hold for as little time as possible
fn process_quickly(data: &Arc>>) {
    let item = {
        let locked = data.lock().unwrap();
        locked.first().cloned() // Extract data
        // Lock released here
    };
    // Do processing outside the lock
    if let Some(x) = item { println!("Processing: {}", x * 2); }
}

🎯 Practice

  1. Implement a thread-safe counter with Arc<Mutex<i64>> - increment from 10 threads simultaneously
  2. Implement a shared cache: Arc<RwLock<HashMap<String, String>>> with concurrent readers and occasional writer

🎉 Key Takeaways