C++ 中多个值的多线程原子 Store/Load

Multithreaded Atomic Store/Load of multiple values in C++

假设我在 C++ 中有一个结构和 class:

struct Vec {
   double x;
   double y;
   double z;
}

class VecTracker {
   Vec latest_vec;
   std::atomic<double> highest_x;
   std::atomic<double> highest_y;
   std::atomic<double> highest_z;

   //updates highest_x, highest_y, highest_z atomically
   void push_vec(const Vec& v);
   double get_high_x() const;
   double get_high_y() const;
   double get_high_z() const;
   //returns Vec consisting of snapshot of highest_x, highest_y, highest_z
   Vec get_highs() const;
}

我将有 R reader 个线程和一个编写器线程。编写器线程将更新零个或多个 highest_* 成员。如果 reader 线程调用 get_highs() 我需要在 reader 线程 之前对 writer 线程的 push_vec() 函数的当前调用中的所有写入可见 reader 线程读取 highest_xhighest_y 等以生成向量。

现在,我知道如果 Vec 足够小,我可以只使用 std::atomic<Vec>。问题是,如果它太大,则无法使用这些 store/loads 的本机 CPU 指令。有什么方法可以使用 std::atomic_thread_fence 来保证在 reader 线程拾取它们之前由编写器线程提交多个原子写入?也就是说,保证写入线程的所有写入都在 reader 线程看到任何写入之前提交?还是 std::atomic_thread_fence 仅在线程内提供重新排序保证?目前,仅对每个成员使用 .store(std::memory_order_release) 似乎并不能保证所有三个存储都发生在任何读取之前。

显然,我可以在这里使用锁,但理想情况下我想找到一种方法使这个数据结构无锁。

我知道我可以将 highest_xhighest_yhighest_z 放在一个结构中,并在堆上分配它的两个副本,在每次写入后自动交换指针。这是唯一的方法吗?

魔鬼来了://updates highest_x, highest_y, highest_z atomically。你如何保证它们确实是原子的?由于 3 个双打不适合 16B(我在 X86_64 平台上知道的最大原子操作),确保这一点的唯一方法是使用 mutex.

你的问题不在于栅栏。通过发出围栏指令,您将保证所有以前的更新都是可见的。但是,您无法保证的是,在 之前它们将不可见。因此,您将能够读取其中一个向量变量的最新值。

要解决您的问题,您应该选择 mutex - 它们在无竞争时非常有效 - 或者,如果您对互斥量过敏,请使用您自己描述的指针交换解决方案。