std::atomic.compare_and_exchange_strong() fails
std::atomic.compare_and_exchange_strong() fails
我对C++很陌生,必须做一些关于原子操作的练习。我正在实现AtomicHashSet,但compare_and_exchange_strong()
的行为与我预期的不同,这让我感到困惑
作为内部数据结构,我使用std::atomic instances:的数组
std::atomic<Item<T>> data[N] = {};
观察问题的关键部分如下:
bool insert(const T &key) {
if (keysStored.load() == N) {
return false;
}
size_t h = this->hash(key);
for (size_t i = 0; i < N; i++) {
size_t pos = (h + i) % N;
data[pos].load(); //No idea why that is needed...
Item<T> atPos = data[pos].load();
if (atPos.dataRef == &key) {
return false;
}
if (atPos.dataRef == nullptr && atPos.state == BucketState::Empty) {
Item<T> atomDesired(&key, BucketState::Occupied);
if (data[pos].compare_exchange_strong(atPos, atomDesired)) {
keysStored++;
return true;
}
}
}
return false;
}
Item
的定义如下:
enum class BucketState { Empty, Occupied, Deleted };
template<typename T>
struct Item {
Item(): dataRef(nullptr), state(BucketState::Empty) {}
Item(const T* dataRef, BucketState state) : dataRef(dataRef), state(state) {}
const T* dataRef;
BucketState state;
};
我做了一些断言测试(插入一个元素两次,检查keyStored
等)。使用这些代码,它们成功了——但如果我删除了无意义的data[pos].load();
调用,它们就会失败,因为compare_exchange_strong()
返回false
。这种奇怪的失败行为只在第一次调用函数时发生。。。
我还用调试器进行了检查——atPos的值与data[pos]
中的值相同——所以在我的理解中,ces应该进行交换并返回true
。
另一个问题:我是否必须使用特殊的内存顺序来确保原子(因此线程安全)行为?
很难说没有mvce,但问题很可能是由于填充引起的。CCD_ 9在概念上使用CCD_。由于对齐要求,您的Item
结构在64位机器上的大小将是16个字节(两个指针),但其中只有12个指针实际对其值有贡献(指针为8,枚举为4)。
所以的声明
Item<T> atPos = data[pos].load();
仅复制前12个字节,但std::atomic.compare_and_exchange_strong
将比较所有16个字节。为了解决这个问题,您可以显式地将BucketState的底层类型指定为与指针大小相同的整数类型(通常size_t和uintptr_t具有该属性)。
例如,项目可能如下所示:
enum class BucketState :size_t { Empty, Occupied, Deleted };
template<typename T>
struct Item {
const T* dataRef;
BucketState state;
static_assert(sizeof(const T*) == sizeof(BucketState), "state should have same size as dataRef");
};
然而,我不能告诉你,为什么使用data[pos].load();
语句会有所不同。如果我没有记错的话,您对std::memcmp的隐式调用会导致未定义的行为,因为它会读取未初始化的内存。
另一个问题:我是否必须使用特殊的内存顺序来确保原子(因此线程安全)行为?
简短的回答是不,你不需要。
长话短说,首先,对std::atomics的访问始终是线程安全的和原子的(它们不一样)。当您希望使用这些原子来保护对非原子共享内存(如if (dataAvalialbe) //readSharedmemory
)的访问时,内存排序变得相关。然而,原子上所有操作的默认内存顺序是memory_order_seq_cst,这是最强的。
- Travis-CI Complex Makefile fails
- OpenCV: fs.open fails
- GetProcAddress fails
- NetUserGetInfo fails
- C++ - bind() fails
- MS Detours - DetourAttach fails
- ld fails to find glfw3
- Visual Studio 2017 .Net Core 2.0 Angular Publish Fails
- SHGetKnownFolderItem - fails on Wow64
- IVssBackupComponents::InitializeForBackup fails
- 如何在编译时检查两种类型是否相同(奖励点是否可与Boost strong typedef一起使用)
- strftime() fails on Raspberry Pi (Raspbian)
- "strong"迭代器指针/引用
- Strong typedefs
- c++ fails to find includes
- ASSERT(AfxGetThread() == NULL); fails
- SSL_CTX_load_verify_locations Fails with SSL_ERROR_NONE
- dijkstra_shortest_paths Boost Graph Lib 1.57.0 fails
- C++ MapViewOfFile fails
- D3D11CreateDeviceAndSwapChain Fails to succeed