【问题标题】:Rust: impl trait cannot be shared between threads safelyRust:impl trait 不能在线程之间安全共享
【发布时间】:2020-05-11 09:44:48
【问题描述】:

我有一个结构RabbitMQBackend,它实现了一个特征Backend,例如:

pub trait Backend {
    // Start consuming message from the queue.
    fn pull(&self, sender: &Sender<String>);
}

pub struct RabbitMQBackend {
    // Some fields ...
}

impl Backend for RabbitMQBackend {
    fn pull(&self, sender: &Sender<String>) {do something...}
}

我正在创建这个结构的一个实例,例如:

let rmq_backend = RabbitMQBackend::new("amqp://user:password@localhost:5672/", "testqueue2");
let mut consumer = ThreadConsumer::new();
consumer.consume(&rmq_backend);

ThreadConsumer 在哪里:

pub struct ThreadConsumer {
    pub sender: Sender<String>,
    pub receiver: Receiver<String>,
}

impl Consumer for ThreadConsumer {
    fn new() -> Self {
        let (sender, receiver) = bounded(3);
        ThreadConsumer {
            sender: sender,
            receiver: receiver,
        }
    }

    fn consume(&mut self, backend: &impl Backend) {
        let consumer = thread::spawn(move || {
            backend.pull(&self.sender);
        });
    }
}

问题是我试图从一个单独的线程中调用backend.pull 函数,但我收到了这个错误:

error[E0277]: `impl Backend` cannot be shared between threads safely
   --> src/consumer/thread_consumer/thread_consumer.rs:23:24
    |
23  |         let consumer = thread::spawn(move || {
    |                        ^^^^^^^^^^^^^ `impl Backend` cannot be shared between threads safely
    |
    = help: the trait `std::marker::Sync` is not implemented for `impl Backend`
help: consider further restricting this bound with `+ std::marker::Sync`
   --> src/consumer/thread_consumer/thread_consumer.rs:20:37
    |
20  |     fn consume(&mut self, backend: &impl Backend) {
    |                                     ^^^^^^^^^^^^
    = note: required because of the requirements on the impl of `std::marker::Send` for `&impl Backend`
    = note: required because it appears within the type `[closure@src/consumer/thread_consumer/thread_consumer.rs:23:38: 25:10 backend:&impl Backend, self:&mut consumer::thread_consumer::thread_consumer::ThreadConsumer]

注意 1:我尝试为 Backend 特征和 RabbitMQBackend 结构实现 Send 和 Senc 特征,例如:

pub trait Backend: Send + Sync {
    // Start consuming message from the queue.
    fn pull(&self, sender: &Sender<String>);
}

pub struct RabbitMQBackend {
    // Some fields ...
}

unsafe impl Send for RabbitMQBackend {}
unsafe impl Sync for RabbitMQBackend {}

然后通过后端函数arg like

fn consume(&amp;mut self, backend: &amp;impl Backend + Send + Sync) {...}

但它引发了以下错误

= note: but, the lifetime must be valid for the static lifetime...
= note: ...so that the types are compatible:

我该如何解决这个问题?

【问题讨论】:

  • 您是否尝试过遵循编译器的建议并添加特征绑定(例如backend: &amp;(impl Backend + Sync))?通常SendSync 标记特征应该手动实现。

标签: rust


【解决方案1】:

原生 Rust 线​​程没有作用域。这意味着一旦线程被生成,它将独立于其创建者生活(就 Rust 编译器而言无论如何)。因此,如果您想将引用移动到线程中,则该引用需要永远(又名'static),因为就编译器而言,创建者线程可能会立即死亡,而子线程可能会立即死亡从来没有。

所以我在那里看到了两个解决方案:

  1. 不要那样做,使用 Arc 之类的东西(可能是互斥锁或 rwlock,具体取决于后端的线程安全)或类似的东西作为后端,这样你的后端就可以通过 Arc 拥有“多个所有者” , 这样你就避免了 ref' 问题

  2. 使用作用域线程

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-10-02
  • 1970-01-01
  • 1970-01-01
  • 2015-02-10
  • 2010-11-23
  • 1970-01-01
相关资源
最近更新 更多