![](/img/trans.png)
[英]When should std::atomic_compare_exchange_strong be used?
[英]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很难说,但是问题很可能是由于填充而引起的。 std::atomic.compare_and_exchange_strong
从概念上使用memcmp
将当前状态与预期状态进行比较。 由于对齐要求,在64位计算机上, Item
结构的大小为16个字节(两个指针),但实际上只有12个字节会影响其值(指针为8,枚举为4)。
所以声明
Item<T> atPos = data[pos].load();
仅复制前12个字节,但std::atomic.compare_and_exchange_strong
将比较所有16个字节。要解决此问题,您可以将BucketState的基础类型明确指定为与指针大小相同的整数类型(通常size_t和uintptr_t具有该属性)。
例如,Item可能如下所示:
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,这是最强的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.