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

ddrv8njm  于 2022-11-12  发布在  其他
关注(0)|答案(1)|浏览(253)

我有一个多线程程序,它想要跨线程访问资源。一些想要写到它们,一些想要从它们读取。
我不确定这是否算作a global mutable singleton,因为我的设置不是全局的,但解决方案可能是类似的?
下面的代码是一个非常简化的版本。
它试图做的是,让一个线程写入一个结构体,另一个线程从同一个结构体读取。因此,当线程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

我已经尝试通过将列表 Package 在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中解决的?

p5fdfcr1

p5fdfcr11#

Arc不允许突变,并且Arc::get_mut()不是该问题的解决方案。当Arc只有一个示例时(因此出现第二个错误),它允许突变,并且返回一个不是'static的引用,因此您无法将其移动到线程中(第一个错误)。
如果您需要改变Arc的内容,请使用MutexRwLock

相关问题