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



这是我在学习 Rust 和遵循Programming Rust时所做的一个实验。

这是操场上代码的链接。

我有一个结构(Thing)和一些内部状态(xs)。应该使用Thing::new创建一个Thing,然后started,之后用户应该选择调用其他一些函数,如get_xs

但!startspawn2 个线程,它们调用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 {
|              ^^^^^

有没有办法生成状态变异线程,并在运行start给使用它的代码后仍然归还thing的所有权?

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

错误消息指出传递给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();
}

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

相关内容

  • 没有找到相关文章