【发布时间】:2015-12-21 10:27:16
【问题描述】:
我正在编写一个 WebSocket 服务器,一个 Web 客户端连接到该服务器以与多线程计算机 AI 下棋。 WebSocket 服务器想要将 Logger 对象传递给 AI 代码。 Logger 对象将把日志行从 AI 传送到 Web 客户端。 Logger 必须包含对客户端连接的引用。
我对生命周期如何与线程交互感到困惑。我用一个类型参数化的Wrapper 结构重现了这个问题。 run_thread 函数尝试解开该值并记录它。
use std::fmt::Debug;
use std::thread;
struct Wrapper<T: Debug> {
val: T,
}
fn run_thread<T: Debug>(wrapper: Wrapper<T>) {
let thr = thread::spawn(move || {
println!("{:?}", wrapper.val);
});
thr.join();
}
fn main() {
run_thread(Wrapper::<i32> { val: -1 });
}
wrapper 参数存在于堆栈中,并且它的生命周期不会超过 run_thread 的堆栈帧,即使线程将在堆栈帧结束之前加入。我可以从堆栈中复制值:
use std::fmt::Debug;
use std::thread;
struct Wrapper<T: Debug + Send> {
val: T,
}
fn run_thread<T: Debug + Send + 'static>(wrapper: Wrapper<T>) {
let thr = thread::spawn(move || {
println!("{:?}", wrapper.val);
});
thr.join();
}
fn main() {
run_thread(Wrapper::<i32> { val: -1 });
}
如果 T 是对我不想复制的大对象的引用,这将不起作用:
use std::fmt::Debug;
use std::thread;
struct Wrapper<T: Debug + Send> {
val: T,
}
fn run_thread<T: Debug + Send + 'static>(wrapper: Wrapper<T>) {
let thr = thread::spawn(move || {
println!("{:?}", wrapper.val);
});
thr.join();
}
fn main() {
let mut v = Vec::new();
for i in 0..1000 {
v.push(i);
}
run_thread(Wrapper { val: &v });
}
结果:
error: `v` does not live long enough
--> src/main.rs:22:32
|
22 | run_thread(Wrapper { val: &v });
| ^ does not live long enough
23 | }
| - borrowed value only lives until here
|
= note: borrowed value must be valid for the static lifetime...
我能想到的唯一解决方案是使用Arc。
use std::fmt::Debug;
use std::sync::Arc;
use std::thread;
struct Wrapper<T: Debug + Send + Sync + 'static> {
arc_val: Arc<T>,
}
fn run_thread<T: Debug + Send + Sync + 'static>(wrapper: &Wrapper<T>) {
let arc_val = wrapper.arc_val.clone();
let thr = thread::spawn(move || {
println!("{:?}", *arc_val);
});
thr.join();
}
fn main() {
let mut v = Vec::new();
for i in 0..1000 {
v.push(i);
}
let w = Wrapper { arc_val: Arc::new(v) };
run_thread(&w);
println!("{}", (*w.arc_val)[0]);
}
在我的实际程序中,似乎Logger 和连接对象都必须放在Arc 包装器中。当代码被并行化的库内部时,客户端需要将连接装箱Arc 似乎很烦人。这特别烦人,因为连接的生命周期肯定会比工作线程的生命周期长。
我错过了什么吗?
【问题讨论】:
标签: multithreading rust reference lifetime