标准库中的基本线程支持允许创建的线程比创建它们的线程存活更久;这是一件好事!但是,如果您将对堆栈分配变量的引用传递给其中一个线程,则无法保证该变量在线程执行时仍然有效。在其他语言中,这将允许线程访问无效内存,从而产生一堆内存安全问题。
一个解决方案是 使用作用域线程 — 保证在父线程退出之前退出的线程。这可以确保父线程中的堆栈变量在线程的整个持续时间内可用。
Rust 1.63
std::thread::scope
移除 回归 , 重返稳定 Rust 。
use std::{thread, time::Duration};
fn main() {
let mut vec = vec![1, 2, 3, 4, 5];
thread::scope(|scope| {
for e in &mut vec {
scope.spawn(move || {
thread::sleep(Duration::from_secs(1));
*e += 1;
});
}
});
println!("{:?}", vec);
}
早期的 Rust 版本或当你需要更多控制时
横梁
我们不局限于标准库;一个流行的作用域线程包是 crossbeam :
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);
}
区
还有像 rayon ,它抽象出了“线程”的低级细节,但允许您实现您的目标:
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);
}
关于示例
每个示例都会生成多个线程,并在没有锁定、没有 Arc
和没有克隆的情况下就地改变本地向量。请注意,改变有一个 sleep
调用,以帮助验证调用是否并行发生。
您可以扩展示例以共享对任何实现 Sync
,例如 a Mutex
或 an Atomic*
。但是,使用这些会引入锁定。
当代码在库内部并行化时, Arc
客户端需要将连接装箱
那么,也许您可以更好地隐藏并行性?您可以接受记录器,然后在 Arc <将其传递给线程之前code>/ Mutex
/