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

如果 orderRelaxed,就会出现 panics。

Examples

如果没有 compiler_fence,则尽管所有内容都在单个线程中发生,但不能保证以下代码中的 assert_eq! 成功。 要了解原因,请记住编译器可以自由地将存储交换为 IMPORTANT_VARIABLEIS_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