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_x
、highest_y
等以生成向量。
现在,我知道如果 Vec
足够小,我可以只使用 std::atomic<Vec>
。问题是,如果它太大,则无法使用这些 store/loads 的本机 CPU 指令。有什么方法可以使用 std::atomic_thread_fence
来保证在 reader 线程拾取它们之前由编写器线程提交多个原子写入?也就是说,保证写入线程的所有写入都在 reader 线程看到任何写入之前提交?还是 std::atomic_thread_fence
仅在线程内提供重新排序保证?目前,仅对每个成员使用 .store(std::memory_order_release)
似乎并不能保证所有三个存储都发生在任何读取之前。
显然,我可以在这里使用锁,但理想情况下我想找到一种方法使这个数据结构无锁。
我知道我可以将 highest_x
、highest_y
和 highest_z
放在一个结构中,并在堆上分配它的两个副本,在每次写入后自动交换指针。这是唯一的方法吗?
魔鬼来了://updates highest_x, highest_y, highest_z atomically
。你如何保证它们确实是原子的?由于 3 个双打不适合 16B(我在 X86_64 平台上知道的最大原子操作),确保这一点的唯一方法是使用 mutex
.
你的问题不在于栅栏。通过发出围栏指令,您将保证所有以前的更新都是可见的。但是,您无法保证的是,在 之前它们将不可见。因此,您将能够读取其中一个向量变量的最新值。
要解决您的问题,您应该选择 mutex
- 它们在无竞争时非常有效 - 或者,如果您对互斥量过敏,请使用您自己描述的指针交换解决方案。
假设我在 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_x
、highest_y
等以生成向量。
现在,我知道如果 Vec
足够小,我可以只使用 std::atomic<Vec>
。问题是,如果它太大,则无法使用这些 store/loads 的本机 CPU 指令。有什么方法可以使用 std::atomic_thread_fence
来保证在 reader 线程拾取它们之前由编写器线程提交多个原子写入?也就是说,保证写入线程的所有写入都在 reader 线程看到任何写入之前提交?还是 std::atomic_thread_fence
仅在线程内提供重新排序保证?目前,仅对每个成员使用 .store(std::memory_order_release)
似乎并不能保证所有三个存储都发生在任何读取之前。
显然,我可以在这里使用锁,但理想情况下我想找到一种方法使这个数据结构无锁。
我知道我可以将 highest_x
、highest_y
和 highest_z
放在一个结构中,并在堆上分配它的两个副本,在每次写入后自动交换指针。这是唯一的方法吗?
魔鬼来了://updates highest_x, highest_y, highest_z atomically
。你如何保证它们确实是原子的?由于 3 个双打不适合 16B(我在 X86_64 平台上知道的最大原子操作),确保这一点的唯一方法是使用 mutex
.
你的问题不在于栅栏。通过发出围栏指令,您将保证所有以前的更新都是可见的。但是,您无法保证的是,在 之前它们将不可见。因此,您将能够读取其中一个向量变量的最新值。
要解决您的问题,您应该选择 mutex
- 它们在无竞争时非常有效 - 或者,如果您对互斥量过敏,请使用您自己描述的指针交换解决方案。