如何使用不安全代码对数组中的同一元素进行多个可变引用?
How to make multiple mutable references to the same element in an array using unsafe code?
我正在尝试用 Rust 制作素筛。
我需要多个对数组相同元素的可变引用,而不是对数组不同部分的可变引用。
对于这种工作方式,我知道数据竞争不是相关问题,因此可以接受多个可变引用,但 Rust 编译器不接受我的不安全代码。
我正在使用 crossbeam 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?
Rust 在其模型中不允许这样做。您想要 AtomicBool
轻松排序。
这在大多数并发模型中是一种有点常见的边缘情况 - 如果您有两个非原子写入 相同的值 到一个位置,这是明确定义的吗?在 Rust(和 C++)中不是,你需要显式地使用 atomic。
On any platform you care about, the atomic bool store with relaxed ordering will have no impact.
为什么这很重要,请考虑:
pub fn do_the_thing(x: &mut bool) {
if !*x { return };
// Do some stuff.
*x = true;
}
在将 x
设置为 true 时,编译器可以自由假设(由于没有共享可变引用)x
仍然是 false。它可以将此赋值实现为,例如,汇编中的 inc x
,将其值从 0 移动到 1。
如果两个线程都通过了这个并且都达到了 *x = true
,它的值可能会变成 0 或 1 以外的值,这可能会违反其他假设的不变量。
我正在尝试用 Rust 制作素筛。
我需要多个对数组相同元素的可变引用,而不是对数组不同部分的可变引用。
对于这种工作方式,我知道数据竞争不是相关问题,因此可以接受多个可变引用,但 Rust 编译器不接受我的不安全代码。
我正在使用 crossbeam 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?
Rust 在其模型中不允许这样做。您想要 AtomicBool
轻松排序。
这在大多数并发模型中是一种有点常见的边缘情况 - 如果您有两个非原子写入 相同的值 到一个位置,这是明确定义的吗?在 Rust(和 C++)中不是,你需要显式地使用 atomic。
On any platform you care about, the atomic bool store with relaxed ordering will have no impact.
为什么这很重要,请考虑:
pub fn do_the_thing(x: &mut bool) {
if !*x { return };
// Do some stuff.
*x = true;
}
在将 x
设置为 true 时,编译器可以自由假设(由于没有共享可变引用)x
仍然是 false。它可以将此赋值实现为,例如,汇编中的 inc x
,将其值从 0 移动到 1。
如果两个线程都通过了这个并且都达到了 *x = true
,它的值可能会变成 0 或 1 以外的值,这可能会违反其他假设的不变量。