我试图编写一个程序,该程序产生一个后台线程,不断地将数据插入到某个集合中。同时,我想继续从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的新手,但这似乎应该是可行的。我无法找到Arc
s,Rc
s,Mutex
es等的正确组合来包装我的数据。
答案 0 :(得分:6)
首先,请阅读Need holistic explanation about Rust's cell and reference counted types。
这里有两个问题需要解决:
要分享所有权,最简单的解决方案是Arc
。它要求其参数为Sync
(可从多个线程安全访问),可以通过将其包含在Send
或Mutex
内来实现任何RwLock
类型。
为了在存在可变性的情况下安全地获取别名,Mutex
和RwLock
都可以正常工作。如果您有多个读者,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
,这意味着当它被锁定时发生恐慌,因此其内容可能是垃圾。