不,这仍然是一个问题.
- 写入并不总是保证是原子的.例如,在32位系统上,
u64
需要多个CPU周期才能写入-因此读取端只能看到更新的值的一半.
- 这超过了soundness,因为编译器无法再证明您的代码没有未定义的行为
的确,像这样访问非常简单的基元类型是安全的.不过,您并不需要static mut
--语言/核心库中内置了一些机制,因此您不必求助于static mut
.在这种情况下,重要的是atomic.
它提供了一种叫做interior mutability的东西.这意味着您的值可以是static
而不是mut
,并且可以正常共享,并且类型本身提供了可变性.
让我来演示一下.由于我现在没有可用的微控制器,我重写了您的示例以实现正常执行:
use core::time::Duration;
static mut ONE_WAY_DATA_EXCHANGE: u8 = 0;
fn main() {
std::thread::scope(|s| {
s.spawn(thread0);
s.spawn(thread1);
});
}
fn thread0() {
std::thread::sleep(Duration::from_millis(500));
unsafe { ONE_WAY_DATA_EXCHANGE = 42 };
}
fn thread1() {
for _ in 0..4 {
std::thread::sleep(Duration::from_millis(200));
let value = unsafe { ONE_WAY_DATA_EXCHANGE };
println!("{}", value);
}
}
0
0
42
42
以下是在使用atomic
的情况下实现的效果:
use core::{
sync::atomic::{AtomicU8, Ordering},
time::Duration,
};
static ONE_WAY_DATA_EXCHANGE: AtomicU8 = AtomicU8::new(0);
fn main() {
std::thread::scope(|s| {
s.spawn(thread0);
s.spawn(thread1);
});
}
fn thread0() {
std::thread::sleep(Duration::from_millis(500));
ONE_WAY_DATA_EXCHANGE.store(42, Ordering::Release);
}
fn thread1() {
for _ in 0..4 {
std::thread::sleep(Duration::from_millis(200));
let value = ONE_WAY_DATA_EXCHANGE.load(Ordering::Acquire);
println!("{}", value);
}
}
0
0
42
42
请注意,代码不包含unsafe
;这完全有效,编译器可以理解,并且(几乎)没有运行时开销.
要演示这实际产生的开销有多小,请执行以下操作:
#![no_std]
use core::sync::atomic::{AtomicU8, Ordering};
static SHARED_VALUE_ATOMIC: AtomicU8 = AtomicU8::new(0);
pub fn write_static_atomic(val: u8){
SHARED_VALUE_ATOMIC.store(val, Ordering::SeqCst)
}
pub fn read_static_atomic() -> u8 {
SHARED_VALUE_ATOMIC.load(Ordering::SeqCst)
}
static mut SHARED_VALUE_STATICMUT: u8 = 0;
pub fn write_static_staticmut(val: u8){
unsafe {
SHARED_VALUE_STATICMUT = val;
}
}
pub fn read_static_staticmut() -> u8 {
unsafe {
SHARED_VALUE_STATICMUT
}
}
代码使用标志-C opt-level=3 -C linker-plugin-lto --target=thumbv6m-none-eabi
编译为the following:
example::write_static_atomic:
dmb sy
ldr r1, .LCPI0_0
strb r0, [r1]
dmb sy
bx lr
.LCPI0_0:
.long example::SHARED_VALUE_ATOMIC.0
example::read_static_atomic:
ldr r0, .LCPI1_0
ldrb r0, [r0]
dmb sy
bx lr
.LCPI1_0:
.long example::SHARED_VALUE_ATOMIC.0
example::write_static_staticmut:
ldr r1, .LCPI2_0
strb r0, [r1]
bx lr
.LCPI2_0:
.long example::SHARED_VALUE_STATICMUT.0
example::read_static_staticmut:
ldr r0, .LCPI3_0
ldrb r0, [r0]
bx lr
.LCPI3_0:
.long example::SHARED_VALUE_STATICMUT.0
example::SHARED_VALUE_ATOMIC.0:
.byte 0
example::SHARED_VALUE_STATICMUT.0:
.byte 0
thumbv6m-none-eabi
上的AtomicU8
英寸似乎几乎没有开销.唯一的变化是dmb sy
,它是防止争用条件的内存屏障;使用Ordering::Relaxed
(如果您的问题允许的话)应该会消除这些障碍,导致实际零开销.其他架构也应该有类似的行为.