没有Arc时何时使用互斥锁?

58

Rust中一个非常常见的模式是Arc<Mutex<T>>,其中Arc提供内存管理,Mutex提供安全的多线程访问资源。还有什么其他东西可以代替Arc,在什么情况下使用?

1个回答

58

Arc是在这个上下文中最常见的一种,但还有其他指针类型可以实现共享。主要的(也是Rust中其余部分中最常见的)是共享引用&T。这通常不适用于使用std::thread::spawn创建的线程,因为它通常指向由其他线程控制的数据,并且通常不是'static(特别是当它是一个&Mutex<T>时)。然而,可以使用scoped thread来创建一个可以与其父进程共享数据的线程。例如:

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]
}
scope.spawn传递的闭包中的data类型事实上是&Mutex<Vec<i32>>(因为它没有使用move关键字,闭包使用的是默认的引用捕获方式)。&Arc是标准库/语言中可以实现此类线程安全共享的两种方法,但也可以编写指针类型,在外部库中提供线程安全共享。然而,移开Pointer<Mutex<...>>模式,将互斥锁和共享分离开来可能会很有用,例如,Arc<Vec<Mutex<T>>>允许分享一些数量的Mutex<T>而不必单独Arc每一个,或者也许想围绕Mutex进行某些抽象,因此将其包装在一个struct中:
struct Wrapped {
    data: Mutex<T>,
}
impl Wrapped {
    // fancy methods that abstract over `data.lock()`
}

那么你可能会看到Arc<Wrapped>(或其他允许共享的指针)。


有没有办法在thread::spawn中使用Mutex而不需要Arc,还是我需要编写自己的包装器?在C/C++中,我不需要引用计数开销就可以在线程内部使用mutex。 - NN_
@NN_ C/C++编译器没有生命周期的概念(除了Herb Sutter为MSVC和Clang提供的可选C++生命周期检查器),因此您可以做任何可能存在竞争条件的事情。Rust曾经有作用域线程,但实现中存在生命周期错误,因此被放弃了。Crossbeam通过在作用域线程周围创建一个作用域来解决生命周期问题,该作用域创建了一个WaitGroup(类似于barrier),将该WaitGroup克隆到每个线程中,然后等待所有对WaitGroup的引用都被丢弃。如果您坚持要这样做,可以复制Crossbeam的解决方案而不实现生命周期安全性。 - Chinoto Vokro

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接