如何在不同的线程中访问可变和不可变的共享内存?



我有一个多线程程序,它想要跨线程访问资源。有些人想给他们写信,有些人想从他们那里读东西。

我不确定这是否算作一个全局可变单例,因为我的设置不是全局的,但解决方案可能是类似的?

一个非常简化的版本是下面的代码:

它试图做的是让一个线程写入一个结构体,另一个线程从同一个结构体中读取。因此,当线程A改变数据时,线程B将读取改变的数据。

use std::{thread, time::Duration};
struct TagList {
list: Vec<String>
}
impl TagList {
fn add(self: &mut TagList, tag: String) {
self.list.push(tag);
}
fn read(&self) -> Vec<String> {
self.list.clone()
}
}
fn main() {
let mut list = TagList { list: vec![] };
thread::spawn(move || {
["fee", "foo", "faa", "fuu"].into_iter().for_each(|tag| {
list.add(tag.to_string());
thread::sleep(Duration::from_millis(100));
});
});
thread::spawn(move || {
loop {
dbg!(list.read());
thread::sleep(Duration::from_millis(20));
}
});
}

很明显,这会导致借用错误:

error[E0382]: use of moved value: `list`
--> src/main.rs:79:19
|
70 |     let mut list = TagList { list: vec![] };
|         -------- move occurs because `list` has type `TagList`, which does not implement the `Copy` trait
71 | 
72 |     thread::spawn(move || {
|                   ------- value moved into closure here
73 |        ["fee", "foo", "faa", "fuu"].into_iter().for_each(|tag| {
74 |          list.add(tag.to_string());
|          ---- variable moved due to use in closure
...
79 |     thread::spawn(move || {
|                   ^^^^^^^ value used here after move
80 |       dbg!(list.read());
|            ---- use occurs due to use in closure

我试图通过在Arc中包装列表来解决这个问题:

use std::sync::Arc;
// ...
let list = Arc::new(TagList { list: vec![] });
let write_list = Arc::get_mut(&mut list).unwrap();
let read_list = Arc::clone(&list);
thread::spawn(move || {
["fee", "foo", "faa", "fuu"].into_iter().for_each(|tag| {
write_list.add(tag.to_string());
thread::sleep(Duration::from_millis(100));
});
});
thread::spawn(move || {
loop {
dbg!(read_list.read());
thread::sleep(Duration::from_millis(20));
}
});

这个失败了,因为我可能不理解Arc应该是如何工作的,或者它与生命周期有什么关系:

error[E0597]: `list` does not live long enough
--> src/main.rs:71:35
|
71 |     let write_list = Arc::get_mut(&mut list).unwrap();
|                      -------------^^^^^^^^^-
|                      |            |
|                      |            borrowed value does not live long enough
|                      argument requires that `list` is borrowed for `'static`
...
85 | }
| - `list` dropped here while still borrowed
error[E0502]: cannot borrow `list` as immutable because it is also borrowed as mutable
--> src/main.rs:72:32
|
71 |     let write_list = Arc::get_mut(&mut list).unwrap();
|                      -----------------------
|                      |            |
|                      |            mutable borrow occurs here
|                      argument requires that `list` is borrowed for `'static`
72 |     let read_list = Arc::clone(&list);
|                                ^^^^^ immutable borrow occurs here

我想要的可能吗?(我很确定我已经见过这种情况,例如std::sync::mpsc,其中以某种方式推送和读取线程的消息)。我应该用什么?Arc是合适的结构,还是我在这里看到错误的解决方案?我应该读些什么来理解Rust通常是如何解决这些问题的?

Arc不允许突变,Arc::get_mut()不是解决方案。当Arc只有一个实例时(因此第二个错误),它允许突变,并返回一个不是'static的引用,因此您不能将其移动到线程中(第一个错误)。

如果需要更改Arc的内容,请使用MutexRwLock