std :: list可以用于简单的无锁队列吗?

时间:2018-09-12 12:13:13

标签: c++ multithreading list qudpsocket

我正在C ++ 11中实现一个多线程应用程序,该应用程序使用一个线程轮询来自QUdpSocket(QT5框架)的数据,并使用另一个线程处理收集的数据。

当套接字检测到传入的数据时,将发出ReadyRead()信号,并从QUdpSocket中提取所有数据报并将其复制到std::list中。

void SocketWrapper::QueuePendingDatagrams()
{    
  while (hasPendingDatagrams()) {
    int dataSize = pendingDatagramSize();
    QByteArray tmpQByte = receiveDatagram().data();
    datagramList.push_back(tmpQByte); // see const_data
  }
  emit newDatagrams(&datagramList);
}

然后在另一个线程中,启动使用者,并检索列表的第一个元素,直到列表为空。

void Worker::ProcessDatagrams(std::list<QByteArray>* pDatagramList)
{
  while (pDatagramList->size() > 0) {
      QByteArray tmpDatagram = pDatagramList->front();
      pDatagramList->pop_front();

      // Process and log the data within the datagram...
}

我的问题是: 由于我总是弹出第一个元素并在最后一个元素之后推送,因此这种无锁方法是否线程安全

在数据处理过程中,我可能(并且将)向套接字接收更多数据,因此两个线程最终将同时运行。在我看来,可能发生的唯一数据竞争可能会高估DatagramList->size(),但我不明白为什么这会成为问题。如果我不断收到数据,我相信整个列表都会被消耗掉。

2 个答案:

答案 0 :(得分:6)

否。

您处于正确的位置,但是实际上您不能同时从不同的线程调用任何容器成员函数,并希望它可靠地工作,因为这些成员函数的实现本身[不保证]是线程安全的。

  

我不明白为什么会出现问题

根据记录,我同意在std::list的特殊情况下,我不会期望此处遇到实际问题。一些商店可能认为这是充分的理由继续进行生产。我不会在代码审查中接受它。关键是我们根本无法*确切地知道内脏做什么。

话虽这么说,如果您有某些特殊的stdlib实现(或带有特殊标志/开关/选项的stdlib实现)确实提供了这种保证,则将您自己淘汰掉;)*最后,您可以检查它的代码并为自己做出决定,但老实说,疯狂就在于此!

答案 1 :(得分:3)

想象一下,列表只有1个元素,而您的2个线程试图“同时”进行推入和弹出操作。

如果您有很多元素,那么从前面弹出一个元素不太可能与后指针交互,并且在后面推一个元素不太可能会干扰前指针,因此/ should /这两个操作是安全的就列表的完整性而言。

但是很明显,当您从最前面弹出最后一个元素时,后面的指针也将得到更新,也就是说没有更多元素,并且当您将一个元素推到后面并且列表为空时,则前指针已更新。您可以保证这些操作的顺序,并且它们不会隐瞒吗?您可以保护大小是否为零,但不能保护大小为1。

与我有关的另一件事是,由于c ++ 11 size()是一个“即时”答案O(0),而不是扫描O(n),因此此列表将保留一个整数。如果push和pop都“同时”修改了尺寸计数器,则它们可能会卡住并最终产生不正确的尺寸值。如果list的内部结构使用size的值作为“ is_empty()”检查,也许是为了优化插入到空列表或弹出最后一个元素,那么它们可能会被愚弄以执行不适当的操作。