C++ std::atomic_thread_fence

2025年3月21日 | 阅读 4 分钟

C++ 标准库提供了函数 std::atomic_thread_fence 来处理原子操作和内存排序。它通过对多线程环境中的内存操作强制执行排序约束,从而防止某些内存操作跨越栅栏被重新排序。std::atomic_thread_fence 函数有几种用法。其中一些如下:

1. 栅栏-原子同步

栅栏-原子同步,如场景中所定义,在线程 B 中的原子获取操作(表示为 Y)和线程 A 中的释放栅栏(表示为 F)之间建立同步关系。在多线程环境中,此同步确保线程 A 中的特定内存操作与线程 B 中的内存操作正确排序,从而防止数据竞争并确保程序正确性。

发生此同步所需的条件如下:

  1. 线程 A 中的释放栅栏(F)
    线程 A 中的此内存栅栏操作确保在其之前的所有内存操作都已完成。它保证在栅栏之前写入的所有数据都可供其他线程访问。
  2. 线程 A 中的原子存储(X)
    这是线程 A 中的原子存储操作,它可能具有任何内存顺序。线程 B 中的原子获取操作读取 X 持有的值。
  3. 线程 B 中的原子获取操作 Y
    线程 B 中的原子获取操作 Y 读取原子存储 X 写入的值。其“获取”内存顺序可防止 Y 在任何其他依赖于从 X 读取的值的线程 B 操作之前被重新排序。
  4. 同步条件
    同步的原因是原子获取操作 Y 和释放栅栏 F 直接相关。在线程 A 中,F 在序列中位于 X 之前。
    Y 读取 X 写入的值,如果 X 是释放操作,则读取以 X 为首的释放序列写入的值。

2. 原子-栅栏同步

在并发编程中,原子-栅栏同步是一种在线程之间提供排序约束的技术。它除了原子操作之外还使用内存栅栏来在线程之间提供正确的同步和数据可见性。发生此同步所需的条件如下:

  1. 线程 A 中的原子释放操作 X
    具有“释放”内存顺序的线程特定原子写入操作称为原子释放操作 X。它确保在 X 之前,线程 A 中所有先前的非原子和宽松原子存储都已完成。
  2. 线程 B 中的原子读取 Y
    此原子读取操作的内存顺序未定义。它读取由以 X 为首的释放序列或原子释放操作 X 写入的值。
  3. 线程 B 中的获取栅栏 F
    获取栅栏确保线程 B 中所有后续操作都不会在栅栏之前被重新排序。
  4. 同步条件
    同步发生是因为原子释放操作 X 和获取栅栏 F 之间存在直接关系
    • Y 读取 X 写入的值(或由以 X 为首的释放序列写入的值)。
    • Y 在线程 B 中在 F 之前排序。

3. 栅栏-栅栏同步

在并发编程中,栅栏-栅栏同步,也称为内存栅栏同步,是一种用于确保线程之间内存操作的正确排序和可见性的技术。内存栅栏或屏障在线程之间创建同步点。

发生此同步所需的条件如下:

  1. 线程 A 中的释放栅栏 (FA)
    线程 A 中的释放栅栏确保在其之前的所有宽松和非原子存储在栅栏之前完成。
  2. 线程 A 中的原子写入 X
    线程 A 通过执行具有任何内存顺序的原子写入操作(例如存储)来修改原子对象 M。在线程 A 中,此操作在释放栅栏 FA 之前。
  3. 线程 B 中的原子读取 Y
    原子读取操作(例如加载)由线程 B 以任何内存顺序执行。此操作读取原子写入操作 X 写入的值,如果 X 是释放操作,则读取以 X 为首的释放序列写入的值。
  4. 线程 B 中的获取栅栏 (FB)
    执行线程 B 中的获取栅栏 FB 是为了保证线程 B 中没有任何操作会在栅栏之前被重新排序。
  5. 同步条件
    以下条件导致同步:
    • 在线程 A 中,FA 在 X 之前排序。
    • Y 读取 X 写入的值或以 X 为首的释放序列写入的值。
    • 在线程 B 中,Y 在 FB 之前排序。

示例

让我们以一个例子来说明 C++ 中的 std::atomic_thread_fence() 方法。

输出

 
The value of x is: 1   

说明

此程序演示了内存栅栏如何通过同步线程之间的内存访问来确保内存操作的正确排序和可见性。更具体地说,栅栏确保线程 1 中对 x 的存储在线程 2 中的加载之前完成,从而防止潜在的数据竞争并确保程序输出的正确性。