免责声明:可能有很多方法来解决这个问题,这只是其中之一,基于@Caesar的 idea .
这篇文章的两个要点:
- 您可以使用
AtomicU32
在线程之间共享f32
,而不会造成任何性能损失(给定u32
已经是原子的体系 struct )
- 你可以用
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);
}
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]
要演示这是多么轻量级,请看here is what this compiles to:
use std::{
sync::atomic::{AtomicU32, Ordering},
};
pub struct AtomicF32(AtomicU32);
impl AtomicF32 {
fn load(&self, order: Ordering) -> f32 {
f32::from_bits(self.0.load(order))
}
fn store(&self, val: f32, order: Ordering) {
self.0.store(val.to_bits(), order)
}
}
pub fn perform_action(value_raw: &AtomicF32) {
let mut value = value_raw.load(Ordering::Relaxed);
value *= 2.5;
value_raw.store(value, Ordering::Relaxed);
}
.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
请注意,虽然这包含零个未定义的行为,但避免读-修改-写争用条件仍然是程序员的责任.