我正在编写一个WebSocket服务器,其中web客户端连接以与多线程计算机AI下棋。 WebSocket服务器想要将Logger对象传递到AI代码中。 Logger对象将把来自AI的日志行传输到Web客户端。 Logger必须包含对客户端连接的引用。
我对生命周期如何与线程交互感到困惑。我使用类型参数化的Wrapper结构复制了问题。 run_thread函数尝试展开值并记录它,但遇到了问题。
这导致了:
我能想到的唯一解决方案是使用一个
在我的实际程序中,似乎必须将
我对生命周期如何与线程交互感到困惑。我使用类型参数化的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
中,这似乎很麻烦,因为连接的生命周期保证大于工作线程的生命周期,而且它是库内部的。我错过了什么吗?
Logger
实现Clone
,并拥有一个类型为Arc<Mutex<Connection>>
的字段。然后用户可以将 logger 的克隆传递给线程化代码。用户无法将Connection
的所有权转移给线程化代码(用户需要它用于其他目的),因此我认为线程化代码无法方便地代表用户执行Arc
和装箱操作。 - Ned RuggeriVec
没有实现Copy
,而这些代码示例中也没有使用Clone
。对于这样的类型,这里呈现的代码可以正常工作。 - Shepmaster