一个可变借用和多个不可变借用

4
我正在尝试编写一个程序,它会生成一个后台线程,不断地向某个集合中插入数据。同时,我想继续从stdin中获取输入,并检查该输入是否在线程正在操作的集合中。
以下是一个简化的示例:
use std::collections::HashSet;
use std::thread;

fn main() {
    let mut set: HashSet<String> = HashSet::new();

    thread::spawn(move || {
        loop {
            set.insert("foo".to_string());
        }
    });

    loop {
        let input: String = get_input_from_stdin();

        if set.contains(&input) {
            // Do something...
        }
    }
}

fn get_input_from_stdin() -> String {
    String::new()
}

然而,由于所有权问题,这种方法行不通。

我对Rust仍然很陌生,但这似乎是可能的事情。我只是找不到正确的ArcRcMutex等组合来包装我的数据。


这可能会对你有所帮助:https://dev59.com/P6Pia4cB1Zd3GeqP3cEw - Netwave
可能是 Rust 中线程安全的可变非拥有指针? 的重复问题。 - Boiethios
1个回答

8
首先,请阅读 需要全面解释 Rust 的 cell 和 reference counted types
这里有两个问题需要解决:
  1. 在不同线程之间共享所有权,
  2. 可变别名。
为了共享所有权,最简单的解决方案是使用 Arc。它需要其参数为 Sync(可以安全地从多个线程访问),通过将其包装在 MutexRwLock 中,可以为任何 Send 类型实现。
在存在可变性别名时,MutexRwLock 都可以保证安全。如果你有多个读取器,则 RwLock 可能具有额外的性能优势。既然只有一个读取器,那就没必要了:让我们使用简单的 Mutex
因此,您的类型是:Arc<Mutex<HashSet<String>>>
下一个技巧是将值传递给在另一个线程中运行的闭包。该值会被“移动”,因此您需要首先克隆 Arc,然后传递克隆版,否则您已经移动了原始值,无法再访问它。
最后,访问数据需要通过借用和锁定...
use std::sync::{Arc, Mutex};

fn main() {
    let set = Arc::new(Mutex::new(HashSet::new()));

    let clone = set.clone();
    thread::spawn(move || {
        loop {
            clone.lock().unwrap().insert("foo".to_string());
        }
    });

    loop {
        let input: String = get_input_from_stdin();

        if set.lock().unwrap().contains(&input) {
            // Do something...
        }
    }
}

调用 unwrap 是因为 Mutex::lock 返回一个 Result;如果被锁定的 Mutex 被损坏,即在锁定期间发生了恐慌并且其内容可能是垃圾,则可能无法锁定 Mutex


正如Mutex“Trait Implementations”文档所述,您不能包装任何类型以获得Sync;您需要包装一个Send类型。 - Stefan
@Stefan:没错!我甚至没有考虑过有人会尝试将非“Send”类型发送到另一个线程,但明确说明肯定更好。 - Matthieu M.

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