将两个对象(其中一个对象持有对另一个对象的引用)传递到线程中

2024-01-28

我有两个对象,其中第二个对象需要第一个对象比它更长寿,因为它保存对第一个对象的引用。我需要将它们移到一个线程中,但编译器抱怨第一个线程的寿命不够长。这是代码:

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对象移动到线程后,引用将不再有效。但假设我想保持结构完整,我无法找到解决这个问题的可行方法。

我也在 Rust 论坛上问过这个问题 http://users.rust-lang.org/t/passing-two-objects-where-one-holds-reference-to-another-into-a-thread/883/5


主要问题是一旦你引用了一个项目,你不能移动该项目 https://stackoverflow.com/q/32300132/155423。让我们看一个简化的内存示例:

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 万岁!

如何解决取决于您的实际情况。在你的例子中,我建议moving the facade进入线程,then创建RoutingNode关于线程堆栈中的引用:

let facade = MyFacade;

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

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

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

我在这里看到几个选项。最直接的是有包装对象取得所有权包装对象的,而不仅仅是一个引用:

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

另一种选择是使用作用域线程 https://stackoverflow.com/q/32750829/155423。这允许您拥有一个可以从闭包外部引用的线程,但是必须加入在借用的变量超出范围之前。作用域线程的两个潜在提供者:

  • 横梁 https://crates.io/crates/crossbeam
  • 作用域线程池 https://crates.io/crates/scoped_threadpool

使用横梁:

extern crate crossbeam;

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

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

如果第一个选项对您的情况具有语义意义,我更喜欢第一个选项。我也喜欢第二个选项,因为线程的生命周期通常不需要是整个程序。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将两个对象(其中一个对象持有对另一个对象的引用)传递到线程中 的相关文章

随机推荐