将包含Arc的self移到新线程中时,为什么会出现“不满足同步”的错误?

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)

Luk*_*odt 5

这是如何运作的?

让我们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,这显然也是您正在努力的部分:

该错误对我而言没有任何意义,因为我没有尝试在线程之间共享它(我正在移动它,而不是克隆它)。

编译器无法知道ArcFoo其实不是共享。考虑一下是否要#[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。而且,如果您仍然“不尝试共享[接收者]”,那么拥有多个所有者毫无意义。