这是我在学习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();
}
}
答案 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。此时程序将编译并运行(尽管工作程序处于无限循环中,因此操场将在超时后终止进程)。