And*_* T. 3 multithreading rust
我有一个结构,该结构包含一个,Arc<Receiver<f32>>并且我试图添加一个方法,该方法获取的所有权self,并将所有权移至新线程并启动它。但是,我得到了错误
error[E0277]: the trait bound `std::sync::mpsc::Receiver<f32>: std::marker::Sync` is not satisfied
--> src/main.rs:19:9
|
19 | thread::spawn(move || {
| ^^^^^^^^^^^^^ `std::sync::mpsc::Receiver<f32>` cannot be shared between threads safely
|
= help: the trait `std::marker::Sync` is not implemented for `std::sync::mpsc::Receiver<f32>`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<std::sync::mpsc::Receiver<f32>>`
= note: required because it appears within the type `Foo`
= note: required because it appears within the type `[closure@src/main.rs:19:23: 22:10 self:Foo]`
= note: required by `std::thread::spawn`
Run Code Online (Sandbox Code Playgroud)
如果我更改结构以容纳Arc<i32>或Receiver<f32>,则它将编译,但不使用Arc<Receiver<f32>>。这是如何运作的?该错误对我而言没有任何意义,因为我没有尝试在线程之间共享它(我正在移动它,而不是克隆它)。
这是完整的代码:
use std::sync::mpsc::{channel, Receiver, Sender};
use std::sync::Arc;
use std::thread;
pub struct Foo {
receiver: Arc<Receiver<f32>>,
}
impl Foo {
pub fn new() -> (Foo, Sender<f32>) {
let (sender, receiver) = channel::<f32>();
let sink = Foo {
receiver: Arc::new(receiver),
};
(sink, sender)
}
pub fn run_thread(self) -> thread::JoinHandle<()> {
thread::spawn(move || {
println!("Thread spawned by 'run_thread'");
self.run(); // <- This line gives the error
})
}
fn run(mut self) {
println!("Executing 'run'")
}
}
fn main() {
let (example, sender) = Foo::new();
let handle = example.run_thread();
handle.join();
}
Run Code Online (Sandbox Code Playgroud)
这是如何运作的?
让我们thread::spawn再次检查需求:
pub fn spawn<F, T>(f: F) -> JoinHandle<T>
where
F: FnOnce() -> T,
F: Send + 'static, // <-- this line is important for us
T: Send + 'static,
Run Code Online (Sandbox Code Playgroud)
由于Foo包含Arc<Receiver<_>>,请检查是否以及如何Arc实现Send:
impl<T> Send for Arc<T>
where
T: Send + Sync + ?Sized,
Run Code Online (Sandbox Code Playgroud)
因此Arc<T>器具Send,如果T器具Send 和 Sync。而在Receiver实现时Send,它没有实现Sync。
那么为什么Arc有如此强烈的要求T呢?T还必须实施,Send因为它Arc可以像容器一样工作;如果您可以隐藏在中未实现Send的内容Arc,则将其发送到另一个线程并在其中解压缩...将会发生不好的事情。有趣的部分是看看为什么T还必须实现Sync,这显然也是您正在努力的部分:
该错误对我而言没有任何意义,因为我没有尝试在线程之间共享它(我正在移动它,而不是克隆它)。
编译器无法知道Arc的Foo其实不是共享。考虑一下是否要#[derive(Clone)]在Foo以后添加一个(这可能没有问题):
fn main() {
let (example, sender) = Foo::new();
let clone = example.clone();
let handle = example.run_thread();
clone.run();
// oopsie, now the same `Receiver` is used from two threads!
handle.join();
}
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,Receiver线程之间只有一个共享。这不好,因为Receiver没有实现Sync!
对我来说,这段代码提出了一个问题:为什么Arc首先要这样做?如您所见,如果没有Arc,它就可以正常工作:您明确声明这Foo是的唯一所有者Receiver。而且,如果您仍然“不尝试共享[接收者]”,那么拥有多个所有者毫无意义。