2

所以我有一个简单的 Qt4 应用程序,它有一个开始按钮、一个停止按钮和一个文本字段。当按下 Start 按钮时,会生成一个新线程,该线程会不断增加一个计数器(在 while 循环中),并且更新文本字段以通过信号/插槽反映这一点。当按下停止按钮时,计数停止,直到再次按下开始按钮。

它工作......有点。它每秒只更新一次计数器;我希望它跑得更快,比如每秒 44100 次。但是,取消 sleep(1) 调用会导致 while 循环阻塞,无法识别任何 GUI 事件,并且应用程序会冻结。此外,停止按钮仅在第二次尝试时有效?

当我对 QThread 进行子类化时,这曾经工作得非常好,但我被告知不要这样做,所以我尝试使用子类化 QObject 然后将对象移动到 QThread 来制作解决方案......但它不能正常工作像以前一样。

这是代码:

工人.h

class Worker : public QObject
{
  Q_OBJECT

  public:
    Worker();

  public slots:
    void process();
    void stopRunning();

  signals:
    void signalValueUpdated(QString);

  private:
    bool running;
};

工人.cpp

#include "Worker.h"

void Worker::process()
{
    qDebug("Worker thread id %d",(int)QThread::currentThreadId());

    static int value=0;
    running = 1;
    while(running == 1)
    {
        QString string = QString("value: %1").arg(value++);
        sleep(1); //I want to take this out or make it way shorter but I can't without it crashing.

        emit signalValueUpdated(string);

        QCoreApplication::processEvents();       
    }
}

void Worker::stopRunning()
{
    qDebug("stopRunning() invoked");
    running = 0;
}

主窗口.h

class MainWindow : public QWidget
{
  Q_OBJECT

  public:
    MainWindow(QWidget *parent = 0);

  private:
    //Widgets
    QHBoxLayout * boxLayout;
    QPushButton * startButton;
    QPushButton * stopButton;
    QLineEdit * lineEdit;

    //Thread where the worker lives
    QThread * workerThread;
    //Worker object itself
    Worker * worker;
};

主窗口.cpp

#include "MainWindow.h"

 MainWindow::MainWindow(QWidget *parent) : QWidget(parent)
{
    boxLayout = new QHBoxLayout(this);
    startButton = new QPushButton("Start Counter", this);
    stopButton = new QPushButton("Stop Counter", this);
    lineEdit = new QLineEdit(this);

    boxLayout->addWidget(startButton);
    boxLayout->addWidget(stopButton); 
    boxLayout->addWidget(lineEdit);

    qDebug("Thread id %d",(int)QThread::currentThreadId());

    workerThread = new QThread;
    worker = new Worker();
    worker->moveToThread(workerThread);

    connect( startButton, SIGNAL(clicked()), workerThread, SLOT(start()), Qt::QueuedConnection ); //When the start button is clicked, start the worker thread
    connect( startButton, SIGNAL(clicked()), worker, SLOT(process()), Qt::QueuedConnection ); //When the start button is clicked, start the worker thread
    connect( workerThread, SIGNAL(started()), worker, SLOT(process()), Qt::QueuedConnection ); //When the worker thread starts, begin the Worker object's process function
    connect( stopButton, SIGNAL(clicked()), worker, SLOT(stopRunning()), Qt::QueuedConnection ); //When the stop button is clicked, invoke the Worker's stopRunning()
    connect( worker, SIGNAL(signalValueUpdated(const QString&)), lineEdit, SLOT(setText(const QString&)), Qt::QueuedConnection ); //When the Worker emits a signalValueChanged, update the lineEdit to reflect this

}

如果没有 Worker.cpp 中的 sleep(1) 和 processEvents(),整个事情就会崩溃,但是有了它们,速度就会变慢,每秒只更新一次数字,而不是 1000 或更多。如何使 while(running) 循环不阻塞?

提前致谢!仍在尝试围绕在 Qt 中进行多线程处理的最佳方法。

4

1 回答 1

2
  1. 如果您不需要它,请在工作人员中删除QCoreApplication::processEvents()(为什么需要它?GUI 事件应该已经由主线程处理......)。这可能是您的问题的原因。

  2. 以正确的方式连接线程信号:

    connect(workerThread, SIGNAL(started()), worker, SLOT(process()));
    connect(startButton, SIGNAL(clicked()), thread, SLOT(start()));
    

    (去掉startButton.clicked()->的连接——worker.process()没用,不做评论里写的)

    为什么要删除它?

    因为将按钮直接连接到worker中的进程是不对的。您应该将按钮连接到 Thread 的开头,然后将 Thread (with started) 连接到 worker process()。避免直接连接应该可以避免您面临的 GUI 冻结问题。

此外,每次单击按钮时,您都需要创建一个新线程并“触发”它。您可以通过将按钮连接到SLOT窗口中的 a 来执行此操作,并从中创建线程(在MainWindow构造函数中创建它将不起作用)。


取自我拥有的(工作)程序:

  // You need another slot in MainWindow, let it be "startProcessing()"
  // in MainWindow::MainWindow, connect the start button to startProcessing()
  connect(btnStart, signal(clicked(), this, SLOT(startProcessing())


  // inside the startProcessing slot
  void MainWindow::startProcessing() {
     ...
     Worker* worker = new Worker;
     QThread* thread = new QThread;

     // start the work
     worker->moveToThread(thread);
     connect(thread, SIGNAL(started()), worker, SLOT(process()));

     // Take care of cleaning up when finished too
     connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
     connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
     connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

     thread->start(); 
  }

您可能已经注意到,我还添加了代码来清理它(deleteLater())。

停止按钮也会给您带来一些问题,但是您现在应该对如何继续有了一个好主意。

于 2013-03-05T08:22:21.317 回答