首页 > 解决方案 > 如何使用不安全代码对数组中的同一元素进行多个可变引用?

问题描述

我正在尝试在 Rust 中制作初筛。

我需要几个对数组相同元素的可变引用,而不是对数组不同部分的可变引用。

对于这种工作方式,我知道数据竞争不是相关问题,因此可以接受多个可变引用,但 Rust 编译器不接受我的不安全代码。

我正在使用横​​梁 0.8.0。

fn extend_helper(
    primes: &Vec<usize>,
    true_block: &mut Vec<bool>,
    segment_min: usize,
    segment_len: usize,
) {
    crossbeam::scope(|scope| {
        for prime in primes {
            let prime = prime.clone();
            let segment_min = &segment_min;
            let segment_len = &segment_len;
            let shared = unsafe { &mut *true_block };

            scope.spawn(move |_| {
                let tmp = smallest_multiple_of_n_geq_m(prime, *segment_min) - *segment_min;
                for j in (tmp..*segment_len).step_by(prime) {
                    shared[j] = false;
                }
            });
        }
    })
    .unwrap();
}

fn smallest_multiple_of_n_geq_m(n: usize, m: usize) -> usize {
    m + ((n - (m % n)) % n)
}
error[E0499]: cannot borrow `*true_block` as mutable more than once at a time
  --> src/lib.rs:12:35
   |
7  |       crossbeam::scope(|scope| {
   |                         ----- has type `&Scope<'1>`
...
12 |               let shared = unsafe { &mut *true_block };
   |                                     ^^^^^^^^^^^^^^^^ `*true_block` was mutably borrowed here in the previous iteration of the loop
13 | 
14 | /             scope.spawn(move |_| {
15 | |                 let tmp = smallest_multiple_of_n_geq_m(prime, *segment_min) - *segment_min;
16 | |                 for j in (tmp..*segment_len).step_by(prime) {
17 | |                     shared[j] = false;
18 | |                 }
19 | |             });
   | |______________- argument requires that `*true_block` is borrowed for `'1`

warning: unnecessary `unsafe` block
  --> src/lib.rs:12:26
   |
12 |             let shared = unsafe { &mut *true_block };
   |                          ^^^^^^ unnecessary `unsafe` block
   |
   = note: `#[warn(unused_unsafe)]` on by default

我应该如何以 Rust 接受的方式编写 unsafe?

标签: rustparallel-processingunsafeunsafemutablepointer

解决方案


Rust 在其模型中不允许这样做。您想要AtomicBool轻松订购。

在大多数并发模型中,这是一种比较常见的边缘情况——如果您有两个具有相同值的非原子写入到一个位置,这是明确定义的吗?在 Rust(和 C++)中它不是,你需要显式地使用 atomic。

在您关心的任何平台上,轻松订购的 atomic bool store 都不会产生影响。


由于这很重要,请考虑:

pub fn do_the_thing(x: &mut bool) {
    if !*x { return };

    // Do some stuff.

    *x = true;
}

在设置x为 true 时,编译器可以自由假设(由于没有共享的可变引用)x仍然为 false。它可以实现这个赋值,例如,inc x在汇编中,将它的值从 0 移动到 1。

如果两个线程都通过 this 并且都达到了*x = true,则它的值可能会变为 0 或 1 以外的值,这可能违反其他假定的不变量。


推荐阅读