Function core::sync::atomic::compiler_fence
1.21.0 · source · pub fn compiler_fence(order: Ordering)
Expand description
编译器内存栅栏。
compiler_fence
不发出任何机器代码,但限制了允许编译器重新排序的内存种类。具体来说,根据给定的 Ordering
语义,可能不允许编译器将调用之前或之后的读取或写入移动到 compiler_fence
的另一侧。请注意,它确实不会阻止 硬件 进行此类重新排序。
在单线程执行上下文中这不是问题,但是当其他线程可以同时修改内存时,则需要更强大的同步原语,例如 fence
。
通过不同的排序语义防止的重新排序是:
- 对于
SeqCst
,不允许在这一点上对读取和写入进行重新排序。 - 对于
Release
,不能将先前的读取和写入移至后续的写入之后。 - 如果使用
Acquire
,则后续的读取和写入操作不能移至先前的读取操作之前。 - 对于
AcqRel
,将同时执行以上两个规则。
compiler_fence
通常仅用于防止线程与自身发生冲突时有用。也就是说,如果给定线程正在执行一段代码,然后被中断,并开始在其他位置执行代码 (虽然仍在同一线程中,并且从概念上讲仍在同一内核上)。在传统程序中,只有在注册信号处理程序时才会发生这种情况。
在更多灵活的代码中,当处理中断,以抢占方式实现绿色线程等时,也会出现这种情况。
鼓励好奇的读者阅读 Linux 内核对 内存屏障 的讨论。
Panics
如果 order
为 Relaxed
,就会出现 panics。
Examples
如果没有 compiler_fence
,则尽管所有内容都在单个线程中发生,但不能保证以下代码中的 assert_eq!
成功。
要了解原因,请记住编译器可以自由地将存储交换为 IMPORTANT_VARIABLE
和 IS_READY
,因为它们都是 Ordering::Relaxed
。如果是这样,并且在更新 IS_READY
之后立即调用信号处理程序,则信号处理程序将看到 IS_READY=1
,但是看到 IMPORTANT_VARIABLE=0
。
使用 compiler_fence
可以解决这种情况。
use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::atomic::Ordering;
use std::sync::atomic::compiler_fence;
static IMPORTANT_VARIABLE: AtomicUsize = AtomicUsize::new(0);
static IS_READY: AtomicBool = AtomicBool::new(false);
fn main() {
IMPORTANT_VARIABLE.store(42, Ordering::Relaxed);
// 防止将较早的写入移至此点之外
compiler_fence(Ordering::Release);
IS_READY.store(true, Ordering::Relaxed);
}
fn signal_handler() {
if IS_READY.load(Ordering::Relaxed) {
assert_eq!(IMPORTANT_VARIABLE.load(Ordering::Relaxed), 42);
}
}
Run