问题:
我有称为资源的对象的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_order和 http://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/