编译器能否对原子指令重新排序,或者原子指令是否充当内存屏障? 再说一遍,写在原子指令之后的指令能在原子指令之前执行吗?
请看下面的代码。如果 useMapA = false
在 mapB
更新和读取线程开始之前移动,我们将使用无效的 mapB
。
注意:更新线程每 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;
}
编辑:我有兴趣以 100% 线程安全换取速度(时间 = 金钱)。这个读取函数需要运行得非常快。您可以假设 15 分钟足够长,以避免如果这个时间短得多会导致的竞争条件。
最佳答案
在回答您的问题之前,我想向您展示如何使用 std::shared_ptr 和原子操作 轻松实现该功能。以下实现是高效且线程安全的。 读者也不需要创建 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);
}
现在,让我们看一下您的代码。这有点复杂。为了方便起见,我们假设 updateMap 每秒调用一次,而不是每 15 分钟调用一次。 useMapA 最初是 true。更新线程执行以下语句,并在更新原子标志之前被中断:
if (useMapA) {
mapB = std::move(*latestMap);
现在,读取器线程只评估原子标志:
bool r1 = useMapA; // r1==true
更新线程继续并将原子标志设置为false。一秒钟后,更新线程评估原子标志:
if (useMapA) { // condition is false
现在,读者线程继续。两个线程都访问 mapA,并且至少有一个线程写入数据结构。这意味着存在数据竞争,这意味着程序的行为是未定义的,无论这种数据竞争是否真的发生。
如果 updateMap 每 15 分钟调用一次,会有什么变化?除非在这 15 分钟内发生一些额外的同步,否则它仍然是一场数据竞赛,因为 C++ 标准在一秒和 15 分钟之间没有区别。
关于c++ - std::atomic 访问是否作为内存屏障?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23002097/