问题描述
我的代码的目标是在线程中发送带有通道 (Data
) 的自定义 std::sync::mpsc::channel
。 Data
实例由 Manager
创建并在 Manager
上有引用。
use std::sync::mpsc;
use std::sync::Arc;
use std::thread;
struct Manager {}
struct Data<'a> {
manager: &'a Manager,}
impl Manager {
fn new() -> Self {
Self {}
}
fn make(&self) -> Data {
Data { manager: self }
}
}
unsafe impl Send for Manager {}
fn main() {
let manager = Arc::new(Manager::new());
let (tx,_rx) = mpsc::channel();
let mut children = Vec::new();
for id in 0..2 {
let thread_manager = Arc::clone(&manager);
let thread_tx = tx.clone();
let child = thread::spawn(move || {
let msg = thread_manager.make();
thread_tx.send(msg).unwrap();
println!("thread {} finished",id);
});
children.push(child);
}
}
error[E0597]: `thread_manager` does not live long enough
--> src/main.rs:36:23
|
33 | let thread_tx = tx.clone();
| --------- lifetime `'1` appears in the type of `thread_tx`
...
36 | let msg = thread_manager.make();
| ^^^^^^^^^^^^^^ borrowed value does not live long enough
37 | thread_tx.send(msg).unwrap();
| ------------------- argument requires that `thread_manager` is borrowed for `'1`
38 | println!("thread {} finished",id);
39 | });
| - `thread_manager` dropped here while still borrowed
但我不明白:
-
let manager = Arc::new(Manager::new());
使用引用计数器创建Manager
的实例 -
let thread_manager = Arc::clone(&manager);
增加 1 个引用计数器,但它是同一个Manager
instance -
let msg = thread_manager.make();
创建一个Data
并引用Manager
对我来说,msg 可以和 manager 有相同的生命周期。
我哪里错了?
谢谢 艾蒂安
解决方法
对我来说,msg 可以和 manager 有相同的生命周期。
是的。这意味着 msg
的生命周期与封闭函数一样长:
let child = thread::spawn(move || {
// <— thread_manager moved into and owned by the function
let msg = thread_manager.make();
thread_tx.send(msg).unwrap();
println!("thread {} finished",id);
// <— thread_manager is dropped here so `msg` becomes invalid
});
不知道何时实际接收/使用消息,但很可能是在线程死亡之后。并且 rustc 不能假设管理器的任何其他实例此时会保持活动状态,因此它必须假设它会留下一个悬空引用。
为什么 msg
不直接使用 Arc<Manager>
,甚至是 Weak<Manager>
? (尽管后者在这里似乎不是一个好主意)。
您可能可以使用范围线程按照您正在尝试的方式做一些事情(我相信甚至没有Arc
),但到目前为止,stdlib 的范围不是由于编译器关注的是 spawn
调用的调用者和内容之间没有生命周期关系,它们完全独立生活,并且可以以任何顺序运行和停止(即使您使用join
)。所以关于线程绑定生命周期的推理完全是本地的。