Rust: impl trait 不能在线程之间安全共享

3

我有一个结构体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 trait 和 RabbitMQBackend 结构体实现Send和Sync trait,如下所示:
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 {}

然后像这样传递了后端函数参数:

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

但是出现了以下错误:

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

我该如何解决这个问题?


你是否尝试过遵循编译器的建议并添加特质约束(例如 backend: &(impl Backend + Sync))?通常情况下,SendSync 标记特质不应该手动实现。 - Aloso
1个回答

2
本地 Rust 线程不是“作用域线程”。这意味着一旦线程被创建,它将独立于其创建者生存(就 Rust 编译器而言)。因此,如果您想将引用移动到线程中,那么该引用需要永久存在(也就是说,要 'static),因为在编译器看来,创建者线程可能会立即死亡,而子线程则不会。
因此,我认为有两种解决方案:
  1. 不要这样做,使用像 Arc 这样的东西(并且可能还需要一个互斥锁或读写锁,具体取决于后端的线程安全性)或其他类似的东西作为您的后端,这样通过 Arc,您的后端就有了“多个所有者”,从而避免了引用问题。

  2. 使用“作用域线程”


我使用了crossbeam库中的scoped threads (https://docs.rs/crossbeam-utils/0.7.2/crossbeam_utils/thread/fn.scope.html)。但是我仍然得到相同的错误。 - Manish Gupta

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接