在线程之间共享对特征实例的引用 [英] Sharing a reference to an instance of trait between threads
问题描述
我正在玩 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));
}
它给了我这些错误:
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
内部的数据是否为 Send
和 Sync
,并且没有这些 trait 共享数据线程可能不安全.您需要指定可以存储在 Arc
中的类型必须是 Send
和 Sync
:
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.
当然,您将只能存储 Foo
的 Sync
和 Send
实现,但这是确保内存安全所必需的.然而,在 Rust 中,同步是通过诸如 Mutex
或 RwLock
之类的包装器实现的.即使 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
绑定了,因为Mutex
是Sync
,如果它的内容是Send
>.
This way, there is no need for the Sync
bound because Mutex
is Sync
if its contents are Send
.
自然,您将无法共享根本不是 Send
的 Foo
实现,并且没有办法绕过它.例如,如果 Foo
的实现包含 Rc
s,就会发生这种情况.
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 Rc
s.
这篇关于在线程之间共享对特征实例的引用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!