我有一个保存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`
如果我更改结构以保存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();
}
这是如何工作的?
让我们再次检查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,
由于Foo
包含一个Arc<Receiver<_>>
,让我们检查一下Arc
是否以及如何实现Send
:
impl<T> Send for Arc<T>
where
T: Send + Sync + ?Sized,
所以Arc<T>
实现Send
如果T
实现Send
和Sync
.虽然Receiver
实现了Send
,但它没有实现Sync
。
那么Arc
为什么对T
有这么强的要求呢?T
还必须实现Send
Arc
因为它可以像容器一样运行;如果您可以隐藏在Arc
中无法实现Send
的东西,请将其发送到另一个线程并在那里解压缩......坏事会发生。有趣的部分是看看为什么T
还必须实现Sync
,这显然也是您正在努力解决的部分:
该错误对我来说没有意义,因为我没有尝试在线程之间共享它(我正在移动它,而不是克隆它(。
编译器无法知道Foo
中的Arc
实际上不是共享的。考虑以后是否会向Foo
添加#[derive(Clone)]
(这可以没有问题(:
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();
}
在上面的示例中,只有一个Receiver
在线程之间共享。这不好,因为Receiver
不实施Sync
!
对我来说,这段代码提出了一个问题:为什么首先要Arc
?正如您所注意到的,如果没有Arc
,它可以毫无问题地工作:您明确指出Foo
是Receiver
的唯一所有者。而且,如果您"无论如何都不想共享[接收器]",那么拥有多个所有者是没有意义的。