56

An extremely common pattern in Rust is Arc<Mutex<T>>, where Arc provides the memory management, and Mutex provides safe multi-threaded access to the resource. What else could be used in place of Arc, and under what circumstances?

Alex Gaynor
  • 14,353
  • 9
  • 63
  • 113

1 Answers1

55

Arc is of course the most common one in this context, but there are other pointer types that allow sharing. The major (and most common, in the rest of Rust) one is the shared reference &T. This normally doesn't work with std::thread::spawn'd threads, because it generally points to data controlled by some other thread, and is hence usually not 'static (particularly so when it is a &Mutex<T>). However, one can use a scoped thread to create a thread that can share data with its parent. E.g.

use crossbeam; // 0.7.3
use std::sync::Mutex;

fn main() {
    let data = Mutex::new(vec![0, 1]);

    crossbeam::scope(|scope| {
        // these run concurrently:
        let _guard = scope.spawn(|_| {
            data.lock().unwrap().push(2);
        });
        data.lock().unwrap().push(3);
    })
    .unwrap();

    println!("{:?}", data.lock().unwrap());
    // one of [0, 1, 2, 3] or [0, 1, 3, 2]
}

The type of data in the closure passed to scope.spawn is in fact &Mutex<Vec<i32>> (since it doesn't have the move keyword the closure is using the default capturing style: by reference).

& and Arc are the two that can achieve this sort of thread-safe sharing in the standard library/language, but one can also write pointer types that offer thread-safe sharing in external libraries.

However, moving away from the Pointer<Mutex<...>> pattern, it can be useful to have the mutex and the sharing separated, e.g. Arc<Vec<Mutex<T>>> allows one to share some number of Mutex<T>'s without having to Arc each one individually, or maybe one wants to have some abstraction around a Mutex, and so wrap it in a struct:

struct Wrapped {
    data: Mutex<T>,
}
impl Wrapped {
    // fancy methods that abstract over `data.lock()`
}

One would likely then see Arc<Wrapped> (or some other pointer that allows sharing).

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
huon
  • 94,605
  • 21
  • 231
  • 225
  • Is it possible in some way to use Mutex without Arc with thread::spawn or I need to write my own wrapper ? In C/C++ I don't need reference count overhead to use mutex inside the thread. – NN_ Feb 27 '19 at 13:31
  • @NN_ C/C++ compilers have no concept of lifetimes (except Herb Sutter's optional C++ lifetime checker for MSVC and Clang), thus you can do whatever potentially racy things you like. Rust used to have scoped threads, but the implementation had a lifetime bug, so it was scrapped. Crossbeam solved the lifetime issue by creating a scope around scoped threads that creates a WaitGroup (similar to barrier), clones that WaitGroup into each thread, and then wait for all references to the WaitGroup to be dropped. You could copy Crossbeam's solution without implementing lifetime safety if you insist. – Chinoto Vokro Mar 27 '19 at 01:05