简体   繁体   English

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

[英]Why do I get an error that "Sync is not satisfied" when moving self, which contains an Arc, into a new thread?

I have a struct which holds an Arc<Receiver<f32>> and I'm trying to add a method which takes ownership of self , and moves the ownership into a new thread and starts it.我有一个包含Arc<Receiver<f32>> ,我正在尝试添加一个方法,该方法获取self的所有权,并将所有权移动到一个新线程中并启动它。 However, I'm getting the error但是,我收到错误

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`

If I change the struct to hold an Arc<i32> instead, or just a Receiver<f32> , it compiles, but not with a Arc<Receiver<f32>> .如果我将结构更改为保存Arc<i32>或仅保存Receiver<f32> ,则它会编译,但不会使用Arc<Receiver<f32>>编译。 How does this work?这是如何运作的? The error doesn't make sense to me as I'm not trying to share it between threads (I'm moving it, not cloning it).该错误对我来说没有意义,因为我不想在线程之间共享它(我正在移动它,而不是克隆它)。

Here is the full code:这是完整的代码:

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();
}

How does this work?这是如何运作的?

Let's check the requirements ofthread::spawn again:让我们再次检查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, 

Since Foo contains an Arc<Receiver<_>> , let's check if and how Arc implements Send :由于Foo包含Arc<Receiver<_>> ,让我们检查Arc是否以及如何实现Send

impl<T> Send for Arc<T> 
where
    T: Send + Sync + ?Sized,

So Arc<T> implements Send if T implements Send and Sync .所以Arc<T>实现了Send如果T实现了SendSync And while Receiver implements Send , it does not implement Sync .虽然Receiver实现了Send但它没有实现Sync

So why does Arc have such strong requirements for T ?那么为什么ArcT有如此强烈的要求呢? T also has to implement Send because Arc can act like a container; T还必须实现Send因为Arc可以充当容器; if you could just hide something that doesn't implement Send in an Arc , send it to another thread and unpack it there... bad things would happen.如果你可以隐藏一些没有在Arc实现Send东西,把它发送到另一个线程并在那里解压......坏事会发生。 The interesting part is to see why T also has to implement Sync , which is apparently also the part you are struggling with:有趣的部分是看看为什么T还必须实现Sync ,这显然也是您正在努力解决的部分:

The error doesn't make sense to me as I'm not trying to share it between threads (I'm moving it, not cloning it).该错误对我来说没有意义,因为我不想在线程之间共享它(我正在移动它,而不是克隆它)。

The compiler can't know that the Arc in Foo is in fact not shared.编译器无法知道Foo中的Arc实际上没有共享。 Consider if you would add a #[derive(Clone)] to Foo later (which is possible without a problem):考虑您是否会稍后向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();
}

In the example above there is only one Receiver which is shared between threads.在上面的例子中,只有一个Receiver在线程之间共享。 And this is no good, since Receiver does not implement Sync !这不好,因为Receiver没有实现Sync

To me this code raises the question: why the Arc in the first place?对我来说,这段代码提出了一个问题:首先为什么是Arc As you noticed, without the Arc , it works without a problem: you clearly state that Foo is the only owner of the Receiver .正如您所注意到的,没有Arc ,它的工作没有问题:您明确声明FooReceiver的唯一所有者。 And if you are "not trying to share [the Receiver]" anyway, there is no point in having multiple owners.而且,如果您“无论如何都不想共享 [接收者]”,那么拥有多个所有者是没有意义的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么我没有得到“跨线程操作无效”错误 - Why do I not get the “Cross-thread operation not valid” error 为什么在使用wcf托管在另一个线程上时,为什么总是总是更改线程ID? - Why do i always get changed Thread Id when hosting on another thread with wcf? 为什么在onCreate()中创建新线程来执行I / O仍会导致错误? - why creating a new Thread to do I/O in onCreate() still causes error? 创建允许多个连接的QTcpServer时,为什么需要在新线程中创建套接字? - When creating a QTcpServer that allows for multiple connections, why do I need to make a socket in a new thread? 当我在单独的线程(实现 Runnable)上运行 class 时,为什么会出现“android.os.NetworkOnMainThreadException”? - Why do I get “android.os.NetworkOnMainThreadException” when I run the class on a separate thread (implementing Runnable)? 为什么在等待我告诉终止的线程时得到“句柄无效”? - Why do I get “The handle is invalid” when waiting for a thread I've told to terminate? 为什么在写入另一个线程上定义的套接字输出时得到(java)NullPointerException? - Why do I get a (java) NullPointerException when writing to a socket's output, defined on another thread? 如何从在线程中运行的循环中获取字符串? - How do I get a String from a loop which runs in a thread? 移动std :: thread时出现链接错误? - Link error when moving a std::thread? 为什么在使用Dispatcher.BeginInvoke()时会出现“无效的跨线程访问”? - Why do I get “Invalid cross-thread access” when using Dispatcher.BeginInvoke()?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM