ube*_*ben 7 multithreading mutex unsafe rust
如何在 Rust 中实现老式多线程(无包装互斥)?为什么这是未定义的行为?
我必须建立一个高度并发的物理模拟。我应该用 C 语言来做,但我选择了 Rust(我确实需要更高级别的功能)。
通过使用 Rust,我应该选择线程之间的安全通信,但是,我必须使用线程之间共享的可变缓冲区。(实际上,我必须实施不同的技术并对它们进行基准测试)
第一种方法
用于Arc<Data>共享非可变状态。
需要时用于transmute推广。&&mut
这很简单,但即使使用块,编译器也会阻止其编译unsafe。这是因为编译器可以应用优化,知道这些数据应该是不可变的(可能被缓存并且从未更新,不是这方面的专家)。
这种优化可以通过Cell包装器和其他方法来停止。
第二种方法
使用Arc<UnsafeCell<Data>>。
然后data.get()访问数据。
这也不能编译。原因是UnsafeCell不是Send。解决方案是使用SyncUnsafeCell但目前不稳定的版本(1.66),程序将在只有稳定版本的机器上编译并投入生产。
第三种方法
使用Arc<Mutex<Data>>。
在每个线程的开头:
锁定互斥体。
*mut通过强制 a 来保留 a &mut。
释放互斥体。
*mut需要时使用
我还没有尝试过这个,但即使它可以编译,它是否安全(不谈论数据竞争)SyncUnsafeCell?
PS:同时变异的值只是f32,绝对没有内存分配或任何复杂的操作同时发生。最坏的情况,我已经打乱了一些f32。
免责声明:可能有很多方法可以解决这个问题,这只是其中之一,基于@Caesar的想法。
这篇文章的两个要点:
AtomicU32共享,f32而不会造成任何性能损失(给定一个已经是原子的架构u32)std::thread::scope来避免 的开销Arc。use std::{
fmt::Debug,
ops::Range,
sync::atomic::{AtomicU32, Ordering},
};
struct AtomicF32(AtomicU32);
impl AtomicF32 {
pub fn new(val: f32) -> Self {
Self(AtomicU32::new(val.to_bits()))
}
pub fn load(&self, order: Ordering) -> f32 {
f32::from_bits(self.0.load(order))
}
pub fn store(&self, val: f32, order: Ordering) {
self.0.store(val.to_bits(), order)
}
}
impl Debug for AtomicF32 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.load(Ordering::Relaxed).fmt(f)
}
}
fn perform_action(data: &Vec<AtomicF32>, range: Range<usize>) {
for value_raw in &data[range] {
let mut value = value_raw.load(Ordering::Relaxed);
value *= 2.5;
value_raw.store(value, Ordering::Relaxed);
}
}
fn main() {
let data = (1..=10)
.map(|v| AtomicF32::new(v as f32))
.collect::<Vec<_>>();
println!("Before: {:?}", data);
std::thread::scope(|s| {
s.spawn(|| perform_action(&data, 0..5));
s.spawn(|| perform_action(&data, 5..10));
});
println!("After: {:?}", data);
}
Run Code Online (Sandbox Code Playgroud)
Before: [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
After: [2.5, 5.0, 7.5, 10.0, 12.5, 15.0, 17.5, 20.0, 22.5, 25.0]
Run Code Online (Sandbox Code Playgroud)
为了演示它的轻量级,下面是它的编译结果:
Before: [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0]
After: [2.5, 5.0, 7.5, 10.0, 12.5, 15.0, 17.5, 20.0, 22.5, 25.0]
Run Code Online (Sandbox Code Playgroud)
.LCPI0_0:
.long 0x40200000
example::perform_action:
movss xmm0, dword ptr [rdi]
mulss xmm0, dword ptr [rip + .LCPI0_0]
movss dword ptr [rdi], xmm0
ret
Run Code Online (Sandbox Code Playgroud)
请注意,虽然这包含零个未定义行为,但程序员仍然有责任避免读取-修改-写入竞争条件。
| 归档时间: |
|
| 查看次数: |
1135 次 |
| 最近记录: |