使用无锁指针队列在线程之间移动数据是否安全

Is it safe to move data between threads using a lock less queue of pointers

本文关键字:移动 之间 数据 是否 安全 线程 指针 队列      更新时间:2023-10-16

我已经实现了一个简单的循环缓冲区,使用(Camerons优秀)readerwriterqueue在线程之间移动数据,以防止在我的应用程序中(取消)分配。代码如下所示:

using ElemPtr = std::unique_ptr<int>;
moodycamel::ReaderWriterQueue<ElemPtr> emptyQueue(10);
moodycamel::ReaderWriterQueue<ElemPtr> dataQueue(10);
LoadQueueWithPointers(emptyQueue);
//If statements removed for brevity
auto producer = [&]() {
    ElemPtr ptr;
    while (true) {
        emptyQueue.try_dequeue(ptr);
        LoadData(ptr);
        dataQueue.try_enqueue(std::move(ptr));
    }
};
//If statements removed for brevity
auto consumer = [&]() {
    ElemPtr ptr;
    while (true) {
        dataQueue.try_dequeue(ptr);
        ProcessData(ptr);
        emptyQueue.try_enqueue(std::move(ptr));
    }
};
std::thread producerThread(producer);
std::thread consumerThread(consumer);

检查此代码时,在我看来,如果使用者线程在 RAM 中更新数据(指针引用)之前收到指针,则数据可能会损坏。我试图使用延迟、不同的队列长度、不同的数据大小以及通过将线程移动到物理上独立的处理器(套接字)来诱导数据损坏。到目前为止,我没有看到任何数据损坏问题。

因此,我的问题是:到目前为止,我是否很幸运,并且存在等待发生的数据损坏问题,或者moodycamel::ReaderWriterQueue使用的内存围栏(std::memory_order_acquirestd::memory_order_release)是否也保护我的(非原子)内存操作?

TL;DR:只要ReaderWriterQueue<>的实现使用具有正确内存顺序的原子类型,就可以了。

您需要的内存排序memory_order_release在编写器端,memory_order_acquire在读取器端。这些内存顺序意味着,原子写入之前的所有写入都需要首先发生,原子读取之后的所有读取都需要稍后发生。这些其他写入/读取包括您对ElemPtr后面数据的访问。

由于队列本身需要将数据从一个线程发送到另一个线程,因此它需要在自己的原子元数据上使用完全相同的内存排序,以便能够发送有效负载数据指针。因此,如果正确实现ReaderWriterQueue<>,您应该是安全的。