将两个对象(其中一个持有对另一个对象的引用)传递到线程中 [英] Passing two objects, where one holds a reference to another, into a thread

查看:54
本文介绍了将两个对象(其中一个持有对另一个对象的引用)传递到线程中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个对象,其中第二个对象需要第一个对象的生命周期更长,因为它持有对第一个对象的引用.我需要将它们都移动到线程中,但是编译器抱怨第一个没有足够长的生存期.这是代码:

I have two objects where the second one requires the fist one to outlive it because it holds a reference to the first one. I need to move both of them into a thread, but the compiler is complaining that the first one doesn't live long enough. Here is the code:

use std::thread;

trait Facade: Sync {
    fn add(&self) -> u32;
}

struct RoutingNode<'a> {
    facade: &'a (Facade + 'a),
}

impl<'a> RoutingNode<'a> {
    fn new(facade: &'a Facade) -> RoutingNode<'a> {
        RoutingNode { facade: facade }
    }
}

fn main() {
    struct MyFacade;

    impl Facade for MyFacade {
        fn add(&self) -> u32 {
            999u32
        }
    }

    let facade = MyFacade;
    let routing = RoutingNode::new(&facade);

    let t = thread::spawn(move || {
        let f = facade;
        let r = routing;
    });

    t.join();
}

游乐场

错误:

error: `facade` does not live long enough
  --> <anon>:27:37
   |
27 |     let routing = RoutingNode::new(&facade);
   |                                     ^^^^^^ does not live long enough
...
35 | }
   | - borrowed value only lives until here
   |
   = note: borrowed value must be valid for the static lifetime...

我相信我知道错误告诉我的内容:一旦 facade 对象移动到线程中,该引用将不再有效.但是,假设我想保持结构完好无损,我找不到解决此问题的可行解决方案.

I believe I understand what the error is telling me: that once the facade object is moved to the thread, the reference will no longer be valid. But I was unable to find a working solution to this problem, assuming I would like to keep the structures intact.

我也在Rust论坛上问了这个问题

推荐答案

主要问题是,一旦您引用了某个项目,您不能移动该项目 .让我们看一下内存的简化示例:

The main problem is that once you have a reference to an item, you cannot move that item. Let's look at a simplified example of memory:

let a = Struct1; // the memory for Struct1 is on the stack at 0x1000
let b = &a;      // the value of b is 0x1000
let c = a;       // This moves a to c, and it now sits on the stack at 0x2000

哦,不,如果我们尝试使用 b 中的引用(仍然指向 0x1000 ),那么我们将访问未定义的内存!这正是Rust可以帮助防止的一类错误-为Rust欢呼!

Oh no, if we try to use the reference in b (which still points at 0x1000), then we will access undefined memory! This is exactly a class of bug that Rust helps prevent - hooray for Rust!

如何修复它取决于您的实际情况.在您的示例中,我建议将 facade 移动到线程中,然后在参考上创建 RoutingNode 在线程的堆栈中:

How to fix it depends on your actual situation. In your example, I'd suggest moving the facade into the thread, then create the RoutingNode on the reference in the thread's stack:

let facade = MyFacade;

let t = thread::spawn(move || {
    let f = facade;
    let r = RoutingNode::new(&f);
});

这是答案的一部分,人们通常会说但演示代码不是我的真实代码所要做的",所以我期待额外的复杂性!

This is the part of the answer where people usually say "but that demo code isn't what my real code does", so I look forward to the extra complexity!

不幸的是,我无法使用此解决方案,因为在将其发送到另一个线程之前,我需要在主线程中使用路由对象

unfortunately I can't use this solution as I need to use the routing object in the main thread prior to sending it to the other thread

我在这里看到一些选择.最简单的方法是让包装对象获得包装对象的所有权,而不仅仅是引用:

I see a few options here. The most straight-forward is to have the wrapping object take ownership of the wrapped object, and not just have a reference:

use std::thread;

trait Facade: Sync {
    fn add(&self) -> u32;
}

struct RoutingNode<F> {
    facade: F,
}

impl<F> RoutingNode<F>
where
    F: Facade,
{
    fn new(facade: F) -> RoutingNode<F> {
        RoutingNode { facade }
    }
}

fn main() {
    struct MyFacade;

    impl Facade for MyFacade {
        fn add(&self) -> u32 {
            999u32
        }
    }

    let facade = MyFacade;
    let routing = RoutingNode::new(facade);

    let t = thread::spawn(move || {
        let r = routing;
    });

    t.join().expect("Unable to join");
}

另一种选择是使用作用域线程 .这样,您就可以拥有一个线程,该线程可以具有来自闭包外部的引用,但是在借入的变量超出范围之前,必须必须加入.作用域线程的两个潜在提供者:

Another option is to use scoped threads. This allows you to have a thread that can have references from outside the closure, but must be joined before the borrowed variables go out of scope. Two potential providers of scoped threads:

使用横梁:

extern crate crossbeam;

let facade = MyFacade;
let routing = RoutingNode::new(&facade);

crossbeam::scope(|scope| {
    scope.spawn(|| {
        let r = routing;
    })
});

如果您的情况在语义上有意义,我更喜欢第一种选择.我也喜欢第二种选择,因为线程的生命周期通常不必是整个程序.

I prefer the first option if it makes semantic sense for your situation. I also like the second option, as often threads have a lifetime that doesn't need to be the entire program.

这篇关于将两个对象(其中一个持有对另一个对象的引用)传递到线程中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆