如何正确停止QThread run()函数?

时间:2016-09-25 12:07:34

标签: multithreading qt thread-safety qml qt5

我有一个线程,生产者和类消费者。消费者在QML注册,生产者与消费者联系。生产者将数据发送给消费者,因此消费者可以更新模型和GUI。

代码看起来像这样:

主要功能:

    int main(int argc, char ** argv)
    {
        QGuiApplication app(argc, argv);
        Producer producer;
        Consumer consumer;

    /* Connect signals between producer and consumer */
    ...
    ...

       QQuickView view;

        /* Set root context */
        QQmlContext *ctxt = view.rootContext();
        producer.start();

        ctxt->setContextProperty("consumer", &consumer);
        /*Connect signals between consumer and QML*/
        ...
        view.show();

        return app.exec();
}

制片人:

class Producer : public QThread
{
    Q_OBJECT
protected:
    void run()
    {
     while(true) {
            if (someFlag == true)
            {
                // do some work
                // emit signal with data to consumer
            }
     }
   }
signals:
// Signals for sending data
};

问题是如何在按下应用程序退出时正确停止线程生产者?

3 个答案:

答案 0 :(得分:2)

而不是while(true)检查"应该结束"条件,例如isInterruptionRequested()

然后,在main()中,在返回之前,告诉线程停止,例如在帖子上使用QThread::requestInterruption然后wait

view.show();

const int ret = app.exec();

producer.requestInterruption();
producer.wait();

return ret;

答案 1 :(得分:0)

我有一个建议:

我的做法是,注册Windows关闭事件并在其中进行任何类型的清洁活动。

下面的伪代码。尽量使其适合您的应用程序。

关闭过滤器事件:

class CloseEventFilter : public QObject
{
     Q_OBJECT
public:
     CloseEventFilter(QObject *parent) : QObject(parent) {}

protected: 
     bool eventFilter(QObject *obj, QEvent *event)
     {
          if (event->type() == QEvent::Close)
          { 
              // Destroy your thread here.
                 producer->exit();//Make your thread available 
               //... may be global....producer is derived from QThread.
          }

          return QObject::eventFilter(obj, event)
     }
}
};

注册到Windows

//Registering window
QGuiApplication *app = new QGuiApplication(argc,argv);
QWindow* qtwindow = new QWindow();
CloseEventFilter *closeFilter = new CloseEventFilter(window);
qtwindow >installEventFilter(closeFilter);

为什么线程退出功能(来自文档)

void QThread :: exit(int returnCode = 0)

告诉线程的事件循环以退回代码退出。

调用此函数后,线程离开事件循环并从调用返回到QEventLoop :: exec()。 QEventLoop :: exec()函数返回returnCode。

按照惯例,returnCode为0表示成功,任何非零值表示错误。

请注意,与同名的C库函数不同,此函数会返回调用者 - 事件处理会停止。

在此线程中不再启动QEventLoops,直到再次调用QThread :: exec()。如果QThread :: exec()中的eventloop没有运行,那么下一次调用QThread :: exec()也会立即返回。

另见quit()和QEventLoop。

答案 2 :(得分:0)

当没有工作要做时,while (true) if (someFlag)循环会将CPU固定在100%。你不想要那个!

唉,没有必要让Producer成为QThread。它可以是一个普通的QObject。有不同的方法。

您可以使用零持续时间计时器使事件循环在线程中保持活动状态:

class Producer : public QObject
{
  Q_OBJECT
  QBasicTimer m_timer;
  void timerEvent(QTimerEvent * ev) override {
    if (ev->timerId() == m_timer.timerId()) 
      doWorkChunk();
  }
  void doWorkChunk() {
    ...
    emit hasData(...);
  }
public:
  Producer(QObject * parent = nullptr) : QObject{parent} {
    connect(this, &Producer::start, this, [this]{ m_timer.start(this, 0); });
    connect(this, &Producer::stop, this, [this]{ m_timer.stop(); });
  }
  Q_SIGNAL void start();
  Q_SIGNAL void stop();
  Q_SIGNAL void hasData(const DataType &);
};

不是设置/清除标记,而是调用start()stop()。然后你可以将生产者移动到任何线程并且它可以正常工作,在退出时做正确的事情(将事情包装起来干净地):

class Thread : public QThread {
  using QThread::run;
public:
  ~Thread() { quit(); wait(); }
};

int main(int argc, char ** argv) {
  QCoreApplication app{argc, argv};
  Producer producer;
  Thread thread; // must come after producer above, the order has meaning!
  producer.moveToThread(&thread);
  thread.start();
  return app.exec();
} 

您也可以使用Qt Concurrent框架:

class Producer : public QObject {
{
  Q_OBJECT
  volatile bool m_active = false;
  void doWorkChunk() {
    ...
    emit hasData();
  }
  void doWork() {
    while (m_active) doWorkChunk();
  }
public:
  using QObject::QObject;
  ~Producer() { stop(); }
  void start() {
    m_active = true;
    QtConcurrent::run(this, &Producer::doWork);
  }
  void stop() {
    m_active = false;
  }
  Q_SIGNAL void hasData(const DataType &);
};