退出前等待插槽执行

Waiting slots to be executed before quitting

我有一个读取数据的线程

class MyThread: QThread
{
  ...
}

void MyThread::run ()
{
  uint8_t* buffer; // in my real code, it's a ring, so there is not read during write
  // ...

  while (true)
  {
    if (isInterruptionRequested())
      return;
    USB_READ(buffer);
    emit newData(buffer);
  }
}

在我的 UI Class 我有:

connect(this, &UIClass::newData, m_thread, &MyThread::newData);

// ...

void newData(uint8_t* data)
{
  // Process data
}

void UIClass::closeEvent(QCloseEvent *event)
{
   disconnect(this, &UIClass::newData, m_thread, &MyThread::newData);
   m_thread->requestInterruption();
   m_thread->wait();
}

问题在于,当我点击 "close" 时,线程被破坏导致指针 data 无效。有时调用信号 newData 会导致我的函数使用无效指针和段错误。如何确定这不会发生?

目前,我使用具有任意延迟的 std::this_thread::sleep_for(),它有效,但我觉得这不是很漂亮

我心里有数 :
- 断开信号
- 等待挂起的信号被执行
- 退出

问题是您将一个指针从一个线程发送到另一个线程而没有确保该指针保持有效。

你有多种选择来解决这个问题。使用 QSharedPointer(或来自 stl 的类似实用程序)来保存您的数据,这样做将确保您的指针保持有效(或者如果您也使用 QWeakPointer,则为您提供一种检测指针何时变为无效的方法)。或者您可以使用 QByteArray 来传递数据,但这会生成一个副本。

示例 1

void MyThread::run ()
{
  QSharedPointer<uint8_t> buffer (new uint8_t[N]()); // Do not delete[], QSharedPointer will handle it
  ...

  emit newData(buffer);

}
void newData(QSharedPointer<uint8_t> data)
{
  // data is always valid
  // Process data
}

示例 2

void MyThread::run ()
{
  QSharedPointer<uint8_t> buffer (new uint8_t[N]());
  ...

  emit newData(buffer);

}
void newData(QWeakPointer<uint8_t> data)
{
  // data might not be valid but we can check
  QSharedPointer<uint8_t> buffer (data);
  if (!buffer)
      return;
  // Process data
}

示例 3

void MyThread::run ()
{
  uint8_t[N] buffer;
  ...

  emit newData(QByteArray(buffer, size));

}
void newData(QByteArray data)
{
  // data is valid
  // Process data
}

您需要做的就是让线程比用户界面更长久。这很简单:

class MyThread : public QThread
{
  Q_OBJECT
  RingBuffer buffer;
public:
  void run() override;
  ~MyThread() {
     requestInterruption();
     quit();
     wait();
  }
  Q_SIGNAL newData(RingBuffer *);
};

int main(int argc, char **argv) {
  QApplication app{argc, argv};
  MyThread thread;
  thread.start();
  UIClass ui;
  connect(&thread, &MyThread::newData, &ui, &UIClass::newData);
  return app.exec();
}