[英]Does std::atomic access serve as a memory barrier?
Can the compiler reorder instructions on atomics, or do atomics serve as a memory barrier? 编译器可以重新排序原子上的指令,还是原子作为内存屏障? Put again, can instructions written after an atomic instruction execute before the atomic instruction?
再说一遍,在原子指令之前执行的指令可以在原子指令之前执行吗?
See the following code. 请参阅以下代码。 If
useMapA = false
is moved before mapB
is updated and the reading thread begins, we will use an invalid mapB
. 如果在
mapB
更新并且读取线程开始之前移动了useMapA = false
,我们将使用无效的mapB
。
Note: the update thread only happens once every 15 minutes, so we have a very well structured flow, and a way to avoid using an expensive lock call! 注意:更新线程每15分钟只发生一次,因此我们有一个结构良好的流程,以及避免使用昂贵的锁定调用的方法!
std::atomic<bool> useMapA;
std::map<string, string> mapA, mapB;
public void updateMap(map<string, string>* latestMap) {
if (useMapA) {
mapB = std::move(*latestMap);
useMapA = false;
} else {
mapA = std::move(*latestMap);
useMapA = true;
}
}
inline map<string, string>& getMap() {
return useMapA ? mapA : mapB;
}
Edit: I am interested in trading away being 100% thread-safe for speed (time = money). 编辑:我有兴趣以100%的线程安全换取速度(时间=金钱)。 This read function needs to run really fast.
这个读取功能需要运行得非常快。 You can assume that 15 minutes is long enough to avoid race conditions that would be caused if this time was much shorter.
您可以假设15分钟足够长,以避免在此时间短得多的情况下引起的竞争条件。
Before answering your question, I would like to show, how you can easily implement the feature with std::shared_ptr and atomic operations . 在回答您的问题之前,我想展示一下,如何使用std :: shared_ptr和原子操作轻松实现该功能。 The following implementation is efficient and thread-safe.
以下实现是高效且线程安全的。 There is also no need for the readers to create a copy of the map.
读者也无需创建地图副本。
using string_map = std::map<std::string, std::string>;
std::shared_ptr<const string_map> map;
std::shared_ptr<const string_map> getMap()
{
return std::atomic_load(&map);
}
void updateMap(string_map latestMap)
{
std::shared_ptr<const string_map> temp{
new string_map{std::move(latestMap)}};
std::atomic_store(&map, temp);
}
Now, let's take a look at your code. 现在,让我们来看看你的代码。 It's a bit more complicated.
这有点复杂。 To make it easier, let's assume that updateMap is called every second instead of every 15 minutes.
为了更容易,我们假设每秒调用updateMap而不是每15分钟调用一次。 useMapA is initially true .
useMapA最初是真的 。 The update thread executes the following statements and will be interrupted before updating the atomic flag:
更新线程执行以下语句,并在更新原子标志之前中断:
if (useMapA) {
mapB = std::move(*latestMap);
Now, a reader thread only evaluates the atomic flag: 现在,读者线程仅评估原子标志:
bool r1 = useMapA; // r1==true
The update thread is continued and sets the atomic flag to false . 继续更新线程并将原子标志设置为false 。 A second later, the update thread evaluates the atomic flag:
一秒钟后,更新线程评估原子标志:
if (useMapA) { // condition is false
Now, the reader thread is continued. 现在,读者线程继续。 Both threads access mapA , and at least one thread writes to the data structure.
两个线程都访问mapA ,并且至少有一个线程写入数据结构。 That means, there is a data race , and that means the behavior of the program is undefined, regardless whether this data race really occurs.
这意味着,存在数据竞争 ,这意味着程序的行为是未定义的,无论此数据竞争是否真的发生。
What changes if the updateMap is called only every 15 minutes? 如果每隔15分钟调用一次updateMap会有什么变化? Unless there happens some additional synchronization within this 15 minutes, it is still a data race, because the C++ Standard doesn't make a difference between a second and 15 minutes.
除非在这15分钟内发生一些额外的同步,否则它仍然是数据竞争,因为C ++标准在第二和第15分钟之间没有区别。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.