共享自引用时无法推断出合适的生命周期

时间:2018-03-28 03:30:50

标签: concurrency rust

这是我在学习Rust和编程Rust 时所做的实验。

这里是link to the code in the playground

我有一个带有内部状态(update-database)的结构(Thing)。应使用xs然后Thing创建Thing::new,然后用户应选择调用start之类的其他功能。

但是!在get_xs 2个线程start编辑调用spawn实例上可能改变其内部状态的其他方法(例如,将元素添加到Thing),因此需要引用xs(因此self)。但是,这会导致终生冲突:

Arc

有没有办法产生状态变异线程,并且在使用error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements --> src/main.rs:18:30 | 18 | let self1 = Arc::new(self); | ^^^^ | note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 17:5... --> src/main.rs:17:5 | 17 | / fn start(&self) -> io::Result<Vec<JoinHandle<()>>> { 18 | | let self1 = Arc::new(self); 19 | | let self2 = self1.clone(); 20 | | ... | 33 | | Ok(vec![handle1, handle2]) 34 | | } | |_____^ note: ...so that expression is assignable (expected &Thing, found &Thing) --> src/main.rs:18:30 | 18 | let self1 = Arc::new(self); | ^^^^ = note: but, the lifetime must be valid for the static lifetime... note: ...so that the type `[closure@src/main.rs:23:20: 25:14 self1:std::sync::Arc<&Thing>]` will meet its required lifetime bounds --> src/main.rs:23:14 | 23 | .spawn(move || loop { | ^^^^^ 代码运行thing之后仍然归还start的所有权?

use std::io;
use std::sync::{Arc, LockResult, RwLock, RwLockReadGuard};
use std::thread::{Builder, JoinHandle};

struct Thing {
    xs: RwLock<Vec<String>>
}

impl Thing {

    fn new() -> Thing {
        Thing {
            xs: RwLock::new(Vec::new()),
        }
    }

    fn start(&self) -> io::Result<Vec<JoinHandle<()>>> {
        let self1 = Arc::new(self);
        let self2 = self1.clone();

        let handle1 = Builder::new()
            .name("thread1".to_owned())
            .spawn(move || loop {
                 self1.do_within_thread1();
            })?;

        let handle2 = Builder::new()
            .name("thread2".to_owned())
            .spawn(move || loop {
                self2.do_within_thread2();
            })?;

        Ok(vec![handle1, handle2])
    }

    fn get_xs(&self) -> LockResult<RwLockReadGuard<Vec<String>>> {
        return self.xs.read();
    }

    fn do_within_thread1(&self) {
        // read and potentially mutate self.xs
    }

    fn do_within_thread2(&self) {
        // read and potentially mutate self.xs
    }
}

fn main() {
    let thing = Thing::new();
    let handles = match thing.start() {
        Ok(hs) => hs,
        _ => panic!("Error"),
    };

    thing.get_xs();

    for handle in handles {
        handle.join();
    }
}

1 个答案:

答案 0 :(得分:1)

错误消息显示传递给Arc的值必须在'static生命周期内生效。这是因为产生一个线程,无论是std::thread::spawn还是std::thread::Builder,都要求传递的闭包在这一生中存在,从而使线程能够自由地生活。超出了产卵线程的范围。

让我们扩展start方法的原型:

fn start<'a>(&'a self: &'a Thing) -> io::Result<Vec<JoinHandle<()>>> { ... }

&'a self放入Arc的尝试会创建一个Arc<&'a Thing>,但仍然会限制为生命周期'a,因此无法移动到关闭需要比这更长寿。由于我们无法移出&self,因此解决方案是不要将&self用于此方法。相反,我们可以start直接接受Arc

fn start(thing: Arc<Self>) -> io::Result<Vec<JoinHandle<()>>> {
    let self1 = thing.clone();
    let self2 = thing;

    let handle1 = Builder::new()
        .name("thread1".to_owned())
        .spawn(move || loop {
             self1.do_within_thread1();
        })?;

    let handle2 = Builder::new()
        .name("thread2".to_owned())
        .spawn(move || loop {
            self2.do_within_thread2();
        })?;

    Ok(vec![handle1, handle2])
}

并在消费者的范围内传递参考计数指针:

let thing = Arc::new(Thing::new());
let handles = Thing::start(thing.clone()).unwrap_or_else(|_| panic!("Error"));

thing.get_xs().unwrap();

for handle in handles {
    handle.join().unwrap();
}

Playground。此时程序将编译并运行(尽管工作程序处于无限循环中,因此操场将在超时后终止进程)。