繁体   English   中英

std :: atomic.compare_and_exchange_strong()失败

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM