Back to Home

Arc and Mutex for parallel state in Rust

The article covers the use of Arc and Mutex for safe access to shared state in multithreaded Rust applications. It includes a counter example, smart pointers comparison, and optimization recommendations. Suitable for middle/senior developers.

Parallel state in Rust: Arc + Mutex in practice
Advertisement 728x90

Safe Shared State Access in Multithreaded Rust Applications

Unprotected memory sharing between threads leads to race conditions, where concurrent reads and writes corrupt data. Even with synchronization, deadlocks can occur, slowing execution. Debugging such code is challenging, though Rust prevents many errors at compile time.

In high-load systems like counters or caches, shared access is justified when using proper synchronization primitives.

Arc and Mutex for Thread-Safe Data Sharing

For safe data transfer between threads, combine Arc<T> and Mutex<T>. Arc<T> provides atomic reference counting with Send and Sync support, enabling multiple ownership in a multithreaded environment. Mutex<T> guarantees mutual exclusion: only one thread accesses data at a time.

Google AdInline article slot

Arc is cloned for each thread using Arc::clone, and access is obtained via lock(), which returns a MutexGuard. The lock() method may panic with a poisoned mutex, so in production, handle errors instead of using unwrap.

Counter Implementation Example

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

struct Counter {
    value: i64
}

fn main() {
    let counter = Arc::new(Mutex::new(Counter {value: 0}));

    let increment = Arc::clone(&counter);
    let handler1 = thread::spawn(move || {
        for _ in 0..10 {
            let mut guard = increment.lock().unwrap();
            guard.value += 1;
            println!("inc: {}", guard.value);
            thread::sleep(Duration::from_millis(100));
        }
    });

    let reader = Arc::clone(&counter);
    let handler2 = thread::spawn(move || {
        for _ in 0..15 {
            let guard = reader.lock().unwrap();
            println!("read: {}", guard.value);
            thread::sleep(Duration::from_millis(180));
        }
    });

    handler1.join().unwrap();
    handler2.join().unwrap();

    let final_guard = counter.lock().unwrap();
    println!("final: {}", final_guard.value);
}

This code demonstrates incrementing in one thread and reading in another. The final value is always 10, with no data races.

Comparison of Key Smart Pointers

Rust offers several smart pointers for memory management. The choice depends on thread safety and ownership requirements:

Google AdInline article slot
  • Box<T>: Single ownership on the heap, suitable for types of unknown size, not Send + Sync.
  • Rc<T>: Multiple ownership within a single thread, not thread-safe.
  • Arc<T>: Multiple ownership across threads, Send + Sync, with overhead from atomic operations.
  • RefCell<T>: Interior mutability with runtime borrowing checks, single-threaded.
  • Cell<T>: Lightweight mutability for Copy types, no panics.
  • Mutex<T>: Locking for exclusive access, risk of deadlocks.
  • RwLock<T>: Optimization for read-heavy scenarios.
  • Weak<T>: Weak references to break cycles in Rc/Arc.
  • ManuallyDrop<T>: Manual destructor control, unsafe.

Arc<T> is unique in std for thread-safe multiple ownership.

Mutex Alternatives for Optimization

In scenarios with frequent reads, consider RwLock<T>: multiple readers can proceed in parallel, writers are exclusive. Performance is higher when the reader/writer ratio > 1.

For lock-free access, use atomics (std::sync::atomic) if data are primitives like AtomicI64. They avoid locks entirely.

Google AdInline article slot

Key Takeaways

  • Shared access is only justified in high-load systems with shared data; otherwise, prefer ownership transfer.
  • Always combine Arc<T> with Mutex<T> or RwLock<T> for thread safety.
  • Avoid unwrap() on lock(): handle PoisonError.
  • Arc<T> carries overhead from atomic operations—measure in benchmarks.
  • For simple cases, use channels (mpsc) instead of shared state.

— Editorial Team

Advertisement 728x90

Read Next