c++ - 使用原子指令确保映射访问安全,是否可以使用 2 个不同的原子指令重新排序?

标签 c++ multithreading c++11

问题:

我有称为资源的对象的std::map。资源是一个对象,它支持资源内部成员的设置/获取操作。 set OR get 操作还会做许多其他性能要求很高的事情。 我还需要支持从 map 本身删除资源的操作。因此,有可能在资源对象上进行设置/获取操作。资源本身被删除,这会导致内存损坏。 删除资源操作很少见。十亿分之一。

我试过使用pthread读写锁来实现线程一致性,但是对性能有影响。后来我尝试用atomics来解决这个问题。这是代码。

std::atomic<bool> g_changeInProgress{false}; // used to block all reader threads
std::atomic<int> g_readers{0}; // used to block delete thread

读者线程

LOOP:
   while(g_changeInProgress) {
        std::this_thread::yield(); // give opportunity to schedule to other threads
    }    
    g_readers++;
    if(g_changeInProgress) {
        g_readers--;
        goto LOOP;
    }   
// DO SET/GET opration with the resource 
// this portion should not execute in parallel to delete
g_readers--;

删除话题

g_changeInProgress = true; 
while(g_readers) {} // busy loop untill no readers left 
/* Delete the Resource here */ 
g_changeInProgress = false;

这个代码片段对我来说似乎工作得很好,而且比 pthread 读写锁快得多。 问题:在删除线程中,编译器是否有可能对指令重新排序,从而导致此代码严重失败?

还有比这更轻的原子锁实现吗?

最佳答案

std::atomic 的默认内存排序是顺序一致性。这意味着对于原子加载/存储,在单个线程内不能对加载或存储进行重新排序。其他线程通过访问相同的原子变量来建立顺序。顺序一致性表示可以跨所有位置、跨所有线程构建所有加载和存储的单一排序。所以实际上你所设想的是有保证的。请参阅:http://en.cppreference.com/w/cpp/atomic/memory_orderhttp://en.cppreference.com/w/cpp/language/memory_model .

可以使用不太严格的内存顺序来提高性能。

如果有多个编写器,此代码将失败。否则我认为它有效,但我没有试图证明这是如此。一般来说,我喜欢在每一行代码中都有明显的不变量,这在这里并不十分清楚。我建议您在建立内存排序属性后仔细检查并明确说明这些内容以构建正确性证明。

更好的方法是使用单个 32 位或 64 位整数和一些高位来跟踪状态,然后使用低位来跟踪阅读器计数。使用 std::atomic<T>::compare_exchange_weak可能还有 fetch_*更新状态的操作。比照SharedExclusiveSpinLock在这个对 Halide 的拉取请求中的实现:https://github.com/halide/Halide/pull/2420/files#diff-e05149e4d7a77708058562163bf8984d .

关于c++ - 使用原子指令确保映射访问安全,是否可以使用 2 个不同的原子指令重新排序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46904242/

相关文章:

c++ - 如何使用NvAPI_DISP_GetDisplayConfig?

c++ - 如何在 C++ 中获取 Windows 7 中所有电源方案的名称?

java - 有状态 session bean 多线程访问

c++ - 如何在 C++ 类中存储智能指针列表?

c++ - 从 QMetaObject 检索 QMetaType

c++ - 为什么我应该使用 const T& 而不是 const T 或 T&

c++ - 为什么需要在此模板中使用 typedef?

c - pthread_rwlock_rdlock 导致读取器数量变为负数

c++ - 传递给新线程的结构有错误的值

c++ - 使用constexpr成员函数初始化constexpr成员变量