我正在使用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));
}
它给了我这些错误:
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那样处理特征和线程。
答案 0 :(得分:12)
请记住,将删除转换为特征对象的原始值类型。因此,编译器无法知道Arc<Foo>
内的数据是Send
还是Sync
,并且没有这些特性跨线程共享数据可能不安全。您需要指定可以Arc<Foo>
存储的类型必须是Send
和Sync
:
let mut map: HashMap<u8, Arc<Foo + Sync + Send>> = HashMap::new();
(试试here)
Send
需要thread::spawn()
绑定,Sync
需要Arc
才能Send
。thread::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
,则Send
为Foo
。
当然,您将无法共享Send
根本不是Foo
的实现,而且无法绕过它。例如,如果Rc
的实现包含private List<Attribute> attributeList;
//setter getters of the attributeList
s。
答案 1 :(得分:8)
弗拉基米尔在答案的前半部分为您的问题提供了一个解决方案:告诉Rust您的HashMap
包含Foo
和Send
Sync
。或者,您可以更改Foo
本身的定义以包含这些特征边界:
trait Foo: Sync + Send {
fn get_foo(&self) -> u8;
}
由于struct A
确实是Send
和Sync
,并且struct A
确实实现了trait Foo
,所以当您使用{Arc<A>
时,类型检查器不会抱怨{1}} Arc<Foo>
。
如果您想要共享 mutable (原子引用计数){{1您需要控制对Foo
的访问权限。这可以使用例如完成。一个Foo
。由于Foo
将负责同步,因此可以删除Mutex
上的Mutex
绑定。例如:
Sync