在Programming Rust的第465页上,您可以找到代码和说明(我加了强调)
use std::sync::Arc; fn process_files_in_parallel(filenames: Vec<String>, glossary: Arc<GigabyteMap>) -> io::Result<()> { ... for worklist in worklists { // This call to .clone() only clones the Arc and bumps the // reference count. It does not clone the GigabyteMap. let glossary_for_child = glossary.clone(); thread_handles.push( spawn(move || process_files(worklist, &glossary_for_child)) ); } ... }
我们已经更改了词汇表的类型:要并行运行分析,调用者必须通过执行
Arc<GigabyteMap>
来传递GigabyteMap
,该智能指针指向已移入堆中的Arc::new(giga_map)
。当我们调用lossary.clone()时,我们正在复制Arc
智能指针,而不是整个GigabyteMap
。这相当于增加参考计数。进行此更改后,程序将编译并运行,因为它不再依赖于参考生存期。 只要任何线程拥有一个Arc<GigabyteMap>
,它都会使映射保持活动状态,即使父线程提早解救了。不会有任何数据争用,因为Arc
中的数据是不可变的。
在下一节中,他们显示了用人造丝重写的内容,
extern crate rayon; use rayon::prelude::*; fn process_files_in_parallel(filenames: Vec<String>, glossary: &GigabyteMap) -> io::Result<()> { filenames.par_iter() .map(|filename| process_file(filename, glossary)) .reduce_with(|r1, r2| { if r1.is_err() { r1 } else { r2 } }) .unwrap_or(Ok(())) }
您可以在改写为使用人造丝的部分中看到,它接受&GigabyteMap
而不是Arc<GigabyteMap>
。他们没有解释这是如何工作的吗?我想知道为什么Ryan不需要Arc<GigabyteMap>
? Rayan如何摆脱接受直接引用的麻烦?
Rayon可以保证迭代器不会超过当前的堆栈帧,这与我在第一个代码示例中假设的是thread::spawn
不同。具体而言,引擎盖下的par_iter
使用类似于Rayon的scope
函数的功能,该函数允许生成“附加”到堆栈的工作单元,并在堆栈结束之前加入。