我正在使用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));
}
Run Code Online (Sandbox Code Playgroud)
(游乐场)
它给了我这些错误:
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`
Run Code Online (Sandbox Code Playgroud)
有人可以推荐一种方法来完成这项任务吗?我想我有点坚持使用Rust的方式来处理特征和线程.
Vla*_*eev 14
请记住,将删除转换为特征对象的原始值类型.因此,编译器无法知道里面的数据是否Arc<Foo>就是Send和Sync,没有这些特质在线程之间共享数据可能是不安全的.你需要指定其可以存储在类型Arc<Foo>必须Send和Sync:
let mut map: HashMap<u8, Arc<Foo + Sync + Send>> = HashMap::new();
Run Code Online (Sandbox Code Playgroud)
(在这里试试)
该Send绑定是必需的thread::spawn(),而且Sync是由需要Arc为它是Send.此外,thread::spawn()还需要,'static但它隐含在此特定Arc<Foo + Sync + Send>类型声明中.
当然,您只能存储Sync和Send实现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();
Run Code Online (Sandbox Code Playgroud)
这样一来,就没有必要为Sync界,因为Mutex是Sync如果其内容Send.
当然,您将无法共享Foo完全没有的实现Send,并且无法解决它.例如,如果Foo实现包含Rcs ,则可能发生这种情况.
弗拉基米尔在答案的前半部分为你的问题提供了一个解决方案:告诉Rust你的HashMap包含Foos Send和Sync.或者,您可以更改Foo自身的定义以包括这些特征边界:
trait Foo: Sync + Send {
fn get_foo(&self) -> u8;
}
Run Code Online (Sandbox Code Playgroud)
因为struct A确实是Send和Sync,由于struct A确实执行trait Foo,当您使用的类型检查器不会抱怨Arc<A>作为Arc<Foo>.
如果不是共享对s的不可变(原子引用计数)引用,Foo而是希望共享对s的可变(原子引用计数)引用Foo,则需要控制对Foos的访问.这可以使用例如a来完成Mutex.由于Mutex然后将处理同步,因此可以删除Sync绑定Foo.例如:
use std::{
collections::HashMap,
sync::{Arc, Mutex},
thread,
time::Duration,
};
#[derive(Debug)]
struct A {
foo: u8,
}
trait Foo: Send {
fn get_foo(&self) -> u8;
}
impl Foo for A {
fn get_foo(&self) -> u8 {
self.foo
}
}
fn main() {
let a = Arc::new(Mutex::new(A { foo: 8 }));
let mut map: HashMap<u8, Arc<Mutex<Foo>>> = HashMap::new();
map.insert(8u8, a);
for _ in 0..2 {
let a = map.get(&8u8).expect("boom").clone();
thread::spawn(move || {
let result = a.lock().ok().expect("boom indeed").get_foo();
println!("Result: {}", result);
});
}
thread::sleep(Duration::from_millis(200));
}
Run Code Online (Sandbox Code Playgroud)
(游乐场)
| 归档时间: |
|
| 查看次数: |
2263 次 |
| 最近记录: |