在线程之间共享对特征实例的引用 [英] Sharing a reference to an instance of trait between threads

查看:30
本文介绍了在线程之间共享对特征实例的引用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在玩 Rust 的并发性,并试图将我的头放在 Send/Sync/Arc/Mutex.我在共享对 HashMap 中保存的 trait 实例的引用时遇到问题:

I am playing with Rust's concurrency and trying to wrap my head around Send/Sync/Arc/Mutex. I have problems with sharing a reference to an instance of trait which is held in a 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 处理特征和线程的方式.

Could anyone please recommend an approach for this task? I think I'm kinda stuck with Rust's way to work with traits and threading.

推荐答案

请记住,转换为 trait 对象的原始值类型会被删除.因此,编译器无法知道 Arc 内部的数据是否为 ​​SendSync,并且没有这些 trait 共享数据线程可能不安全.您需要指定可以存储在 Arc 中的类型必须是 SendSync:

Remember that types of original values which are converted to trait objects are erased. Therefore, the compiler can't know whether the data inside the Arc<Foo> is Send and Sync, and without these traits sharing data across threads may be unsafe. You need to specify that types which can be stored in Arc<Foo> must be Send and Sync:

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

(试试这里)

thread::spawn()需要Send绑定,Arc需要Sync因为它是Send.此外,thread::spawn() 也需要 'static 但它隐含在这个特定的 Arc 类型声明中.

The Send bound is required by thread::spawn(), and Sync is required by Arc for it to be Send. Additionally, thread::spawn() also requires 'static but it is implicit in this particular Arc<Foo + Sync + Send> type declaration.

当然,您将只能存储 FooSyncSend 实现,但这是确保内存安全所必需的.然而,在 Rust 中,同步是通过诸如 MutexRwLock 之类的包装器实现的.即使 T 实现了 Foo,它们也不实现 Foo,因此您将无法存储,例如,Mutex<;Foo + Send> 在你的地图中(除非 Foo 是你的 trait 并且你为 Mutex 实现了它,这可能很笨拙),这将是如果您的 Foo 实现不是 Sync 而是 Send(虽然我不确定我现在是否可以提供此类类型的示例),则这是必要的.

Of course, you will be able to store only Sync and Send implementations of Foo, but this is necessary to ensure memory safety. However, in Rust synchronization is implemented with wrappers like Mutex<T> or RwLock<T>. They don't implement Foo even if T implements Foo, therefore you won't be able to store, say, Mutex<Foo + Send> inside your map (unless Foo is your trait and you implemented it for Mutex<Foo>, which could be unwieldy), which would be necessary if your Foo implementations are not Sync but Send (though I'm not sure I can provide an example of such type now).

要解决此问题,您需要更改映射类型以在其中显式包含互斥锁:

To solve this you'd need to change map type to contain a mutex inside it explicitly:

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

这样就不需要Sync绑定了,因为MutexSync,如果它的内容是Send>.

This way, there is no need for the Sync bound because Mutex is Sync if its contents are Send.

自然,您将无法共享根本不是 SendFoo 实现,并且没有办法绕过它.例如,如果 Foo 的实现包含 Rcs,就会发生这种情况.

And naturally, you won't be able to share Foo implementations which are not Send at all, and there is no way around it. This can happen, for example, if Foo's implementation contains Rcs.

这篇关于在线程之间共享对特征实例的引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆