关于rust:并行处理vec:如何安全地进行操作,或者不使用不稳定的功能?

Processing vec in parallel: how to do safely, or without using unstable features?

我有一个庞大的向量,我希望能够并行加载/操作,例如 在一个线程中加载前十万个索引,在另一个线程中加载,依此类推。 由于这将是代码中非常热门的部分,因此我想出了以下以下概念证明不安全的代码,而无需使用Arcs和Mutexes:

1
2
3
4
5
6
7
8
9
10
let mut data:Vec<u32> = vec![1u32, 2, 3];
let head = data.as_mut_ptr();
let mut guards = (0..3).map(|i|
  unsafe {
    let mut target = std::ptr::Unique::new(head.offset(i));
    let guard = spawn(move || {
      std::ptr::write(target.get_mut(), 10 + i as u32);
    });
    guard
  });

我在这里想念的东西有什么可能会使它爆炸吗?

它使用#![feature(unique)],所以我看不到如何稳定地使用它。 有没有一种方法可以稳定地执行这种操作(理想情况下是安全的,无需使用原始指针和ArcMutex的开销)?

另外,在查看Unique的文档时,它说

It also implies that the referent of the pointer should not be modified without a unique path to the Unique reference

我不清楚"独特的道路"是什么意思。


今天,rayon板条箱已成为此类事情的事实上的标准:

1
2
3
4
5
6
7
8
9
use rayon::prelude::*;

fn main() {
    let mut data = vec![1, 2, 3];
    data.par_iter_mut()
        .enumerate()
        .for_each(|(i, x)| *x = 10 + i as u32);
    assert_eq!(vec![10, 11, 12], data);
}

请注意,这与使用标准迭代器的单线程版本仅有一行不同,后者将par_iter_mut替换为iter_mut

另请参见在Rust和Zig中编写一个小型射线跟踪器。


可以为此使用外部库,例如 simple_parallel(免责声明,我写了)允许写:

1
2
3
4
5
6
7
8
9
extern crate simple_parallel;

let mut data = vec![1u32, 2, 3, 4, 5];

let mut pool = simple_parallel::Pool::new(4);

pool.for_(data.chunks_mut(3), |target| {
    // do stuff with `target`
})

chunkschunks_mut方法是将T的向量/片段拆分为大小相等的块的完美方法:它们分别在&[T]&mut [T]类型的元素上返回迭代器。