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

Posted

技术标签:

【中文标题】使用无锁指针队列在线程之间移动数据是不是安全【英文标题】:Is it safe to move data between threads using a lock less queue of pointers使用无锁指针队列在线程之间移动数据是否安全 【发布时间】:2017-11-03 12:34:26 【问题描述】:

我已经实现了一个简单的循环缓冲区,以使用 (Camerons excellent) 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 中更新之前接收到指针,则数据可能会损坏。我尝试使用延迟、不同的队列长度、不同的数据大小以及将线程移动到物理上独立的处理器(套接字)来诱导数据损坏。到目前为止,我还没有发现数据损坏问题。

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

【问题讨论】:

原子加载/存储的内存顺序存在,因为它们控制如何重新排序其他非原子加载/存储。所以,据说,你很好。但是请注意,如果您在强排序 CPU(如 x86)上对此进行测试,那么 CPU 会隐藏许多潜在问题。因此,在保证较弱的不同 CPU(如 ARM)上,您可能会遇到问题。 【参考方案1】:

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

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

由于队列本身需要将数据从一个线程发送到另一个线程,因此它需要对其自己的原子元数据使用完全相同的内存顺序才能发送有效负载数据指针。因此,如果ReaderWriterQueue&lt;&gt; 正确实施,您应该是安全的。

【讨论】:

以上是关于使用无锁指针队列在线程之间移动数据是不是安全的主要内容,如果未能解决你的问题,请参考以下文章

高效无锁环形队列

高效无锁环形队列

高效无锁环形队列

是否存在多个读取或写入线程的无锁队列?

CAS无锁队列与线程同步

C++ 无锁队列与多线程崩溃