作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在编写一个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
中对连接进行装箱。这特别令人讨厌,因为保证了连接的生存期大于工作线程的生存期。
最佳答案
标准库中的线程支持使创建的线程的生命周期超过创建它们的线程的生命周期。这是好事!但是,如果将对分配给堆栈的变量的引用传递给这些线程之一,则不能保证该变量在线程执行时仍然有效。用其他语言,这将允许线程访问无效的内存,从而产生大量的内存安全问题。
幸运的是,我们不仅限于标准库。至少有两个 crate 提供了作用域线程-保证在某个作用域结束之前退出的线程。这些可以确保堆栈变量在整个线程持续时间内可用:
Arc
和没有克隆的情况下就地改变了本地向量。请注意,该变异具有
sleep
调用,以帮助验证调用是否并行发生。
Sync
的任何类型的引用,例如
Mutex
或
Atomic*
。但是,使用这些将导致锁定。
use scoped_threadpool::Pool; // 0.1.9
use std::{thread, time::Duration};
fn main() {
let mut vec = vec![1, 2, 3, 4, 5];
let mut pool = Pool::new(vec.len() as u32);
pool.scoped(|scoped| {
for e in &mut vec {
scoped.execute(move || {
thread::sleep(Duration::from_secs(1));
*e += 1;
});
}
});
println!("{:?}", vec);
}
use crossbeam; // 0.6.0
use std::{thread, time::Duration};
fn main() {
let mut vec = vec![1, 2, 3, 4, 5];
crossbeam::scope(|scope| {
for e in &mut vec {
scope.spawn(move |_| {
thread::sleep(Duration::from_secs(1));
*e += 1;
});
}
})
.expect("A child thread panicked");
println!("{:?}", vec);
}
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; // 1.0.3
use std::{thread, time::Duration};
fn main() {
let mut vec = vec![1, 2, 3, 4, 5];
vec.par_iter_mut().for_each(|e| {
thread::sleep(Duration::from_secs(1));
*e += 1;
});
println!("{:?}", vec);
}
the client is required to box the connection in an
Arc
when it is internal to the library that the code is parallelized
Arc
/
Mutex
中,然后再将其交给您的线程吗?
关于multithreading - 如何将对堆栈变量的引用传递给线程?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64503724/
我是一名优秀的程序员,十分优秀!