【问题标题】:Waiting slots to be executed before quitting退出前等待槽执行
【发布时间】:2017-05-31 09:15:57
【问题描述】:

我有一个读取数据的线程

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 类中,我有:

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();
}

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

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

我的想法:
- 断开信号
- 等待待处理的信号被执行
- 退出

【问题讨论】:

    标签: c++ multithreading qt qthread


    【解决方案1】:

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

    您有多种选择来解决这个问题。使用 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
    }
    

    【讨论】:

    • 谢谢,我尝试了第一个,它正在工作。如果我不想使用动态分配但我想使用地址怎么办:uint8_t buffer[size] ..... emit &buffer[0]。在这种情况下我能做什么,因为我不希望我的指针被删除(它不能)
    • 如果你在堆栈上分配缓冲区(而不是在堆上动态分配)你不能保证它在调用槽时是有效的,因为你不能保证缓冲区仍然在范围在另一个线程中。唯一的解决方案是像第三个示例一样制作副本。
    • 谢谢你,我肯定需要第一个
    【解决方案2】:

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

    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();
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-17
      • 2020-12-12
      • 1970-01-01
      • 2018-03-25
      相关资源
      最近更新 更多