在线程之间共享对特征实例的引用

时间:2015-09-03 07:56:10

标签: rust

我正在使用Rust的并发性,并尝试围绕Send / Sync / Arc / Mutex。我在共享对HashMap

中保存的特征实例的引用时遇到问题
use std::{collections::HashMap, sync::Arc, thread, time::Duration};

#[derive(Debug)]
struct A {
    foo: u8,
}

trait Foo {
    fn get_foo(&self) -> u8;
}

impl Foo for A {
    fn get_foo(&self) -> u8 {
        self.foo
    }
}

fn main() {
    let a = Arc::new(A { foo: 8 });

    let mut map: HashMap<u8, Arc<Foo>> = HashMap::new();
    map.insert(8u8, a);

    for _ in 0..2 {
        let a = map.get(&8u8).expect("boom");
        let a = a.clone();
        thread::spawn(move || {
            let _ = a.get_foo();
        });
    }
    thread::sleep(Duration::from_millis(200));
}

playground

它给了我这些错误:

error[E0277]: `dyn Foo` cannot be sent between threads safely
  --> src/main.rs:27:9
   |
27 |         thread::spawn(move || {
   |         ^^^^^^^^^^^^^ `dyn Foo` cannot be sent between threads safely
   |
   = help: the trait `std::marker::Send` is not implemented for `dyn Foo`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<dyn Foo>`
   = note: required because it appears within the type `[closure@src/main.rs:27:23: 29:10 a:std::sync::Arc<dyn Foo>]`
   = note: required by `std::thread::spawn`

error[E0277]: `dyn Foo` cannot be shared between threads safely
  --> src/main.rs:27:9
   |
27 |         thread::spawn(move || {
   |         ^^^^^^^^^^^^^ `dyn Foo` cannot be shared between threads safely
   |
   = help: the trait `std::marker::Sync` is not implemented for `dyn Foo`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<dyn Foo>`
   = note: required because it appears within the type `[closure@src/main.rs:27:23: 29:10 a:std::sync::Arc<dyn Foo>]`
   = note: required by `std::thread::spawn`

有人可以为这项任务推荐一种方法吗?我想我有点像Rust那样处理特征和线程。

2 个答案:

答案 0 :(得分:12)

请记住,将删除转换为特征对象的原始值类型。因此,编译器无法知道Arc<Foo>内的数据是Send还是Sync,并且没有这些特性跨线程共享数据可能不安全。您需要指定可以Arc<Foo>存储的类型必须是SendSync

let mut map: HashMap<u8, Arc<Foo + Sync + Send>> = HashMap::new();

(试试here

Send需要thread::spawn()绑定,Sync需要Arc才能Sendthread::spawn()此外,'static还需要Arc<Foo + Sync + Send>,但它隐含在此特定Sync类型声明中。

当然,您只能存储Send的{​​{1}}和Foo个实现,但这对于确保内存安全是必要的。但是,在Rust中,同步是使用Mutex<T>RwLock<T>等包装器实现的。即使Foo实施T,它们也不会实施Foo,因此您无法在地图中存储Mutex<Foo + Send>(除非Foo }是你的特质而你是为Mutex<Foo>实现的,这可能很笨重),如果你的Foo实施不是Sync而是Send,那将是必要的(尽管我'我不确定我现在可以提供这种类型的例子。

要解决此问题,您需要更改地图类型以明确地在其中包含互斥:

let mut map: HashMap<u8, Arc<Mutex<Foo + Send>>> = HashMap::new();

这样就不需要Sync绑定,因为如果Mutex的内容为Sync,则SendFoo

当然,您将无法共享Send根本不是Foo的实现,而且无法绕过它。例如,如果Rc的实现包含private List<Attribute> attributeList; //setter getters of the attributeList s。

,就会发生这种情况

答案 1 :(得分:8)

弗拉基米尔在答案的前半部分为您的问题提供了一个解决方案:告诉Rust您的HashMap包含FooSend Sync。或者,您可以更改Foo本身的定义以包含这些特征边界:

trait Foo: Sync + Send {
    fn get_foo(&self) -> u8;
}

由于struct A确实是SendSync,并且struct A确实实现了trait Foo,所以当您使用{Arc<A>时,类型检查器不会抱怨{1}} Arc<Foo>

如果您想要共享 mutable (原子引用计数){{1您需要控制对Foo的访问权限。这可以使用例如完成。一个Foo。由于Foo将负责同步,因此可以删除Mutex上的Mutex绑定。例如:

Sync

playground