首页 > 解决方案 > 打破在线程中的 QObject 的插槽内调用的循环

问题描述

AcquisitionManager 是一个生活在线程中的 QObject,我使用它从采集 PCI-Express 卡中获取样本:

m_acquisitionThread = new QThread(this);
m_acquisitionManager = new AcquisitionManager();

m_acquisitionManager->moveToThread(m_acquisitionThread);
m_acquisitionThread->start();

在我的应用程序代码(位于主线程中)中,我使用这个:

QMetaObject::invokeMethod(m_acquisitionManager, "executeDataAcquisition", Qt::QueuedConnection);

启动数据采集。

在“executeDataAcquisition”插槽内,我有一个循环:

for (size_t i = 0; i < numberOfLoops; ++i)
{
    // blocking calls...
}

有时,我希望用户尽快中止采集,我考虑使用从主线程修改的布尔变量(+ volatile)。即使这不是非常线程安全的,布尔变量也只能由“从”线程读取,并且只能由主线程(主线程)写入:

for (size_t i = 0; i < numberOfLoops && m_bKeepAcquiring; ++i)
{
    // blocking calls...
}

这样做是否正确?我在我工作的很多软件中都看到了这种技术,但我不知道这样做是否安全。还有其他技术吗?

标签: qt

解决方案


好吧,“即使这不是非常线程安全的”,这确实有点像代码风格问题。我的意思是,如果您不关心直接跨线程访问,那么我想这并不重要。

就我个人而言,我可能不会那样做。向工作线程添加一个方法来设置“停止”标志(甚至使用简单的互斥锁来保护标志,如QReadWriteLock)非常简单。然后可以使用信号或排队的元方法调用“正确地”订购停止。如果除了主控制器线程之外不可能从任何地方设置标志,那么跳过互斥锁可能没问题,尽管它也是相当便宜的保险。

class AcquisitionManager ... {

public slots:
  void requestStop() {
    QWriteLocker lock(&m_flagMutex);
    m_bStopRequested = true;
  }

  void executeDataAcquisition() {
    for (size_t i = 0; i < numberOfLoops && !stopRequested(); ++i) {
      // blocking calls...
      QCoreApplication::processEvents();  // gives a chance for queued requestStop() call to be invoked
    }
  }

private:
  inline bool stopRequested() {
    QReadLocker lock(&m_flagMutex);
    return m_bStopRequested;
  }

  bool m_bStopRequested = false;
  QReadWriteLock m_flagMutex;

};

推荐阅读