C++ Qt4:独立线程块GUI中的while循环
因此,我有一个简单的Qt4应用程序,它有一个开始按钮、一个停止按钮和一个文本字段。当按下开始按钮时,会产生一个新线程,该线程会不断增加计数器(在while循环中),文本字段会更新,以通过信号/插槽反映这一点。按下停止按钮时,计数停止,直到再次按下开始按钮 它是有效的。。。某种程度上。它每秒只更新计数器一次;我希望它跑得更快,比如说每秒44100次。但是,执行sleep(1)调用会导致while循环阻塞,无法识别GUI事件,并且应用程序会冻结。此外,停止按钮仅在第二次尝试时有效 当我对QThread进行子类化时,这项工作非常好,但有人告诉我不要这样做,所以我尝试通过对QObject进行子类化,然后将对象移动到QThread来制定解决方案。。。但它已经不像以前那么好用了 代码如下: Worker.hC++ Qt4:独立线程块GUI中的while循环,c++,multithreading,qt4,qthread,C++,Multithreading,Qt4,Qthread,因此,我有一个简单的Qt4应用程序,它有一个开始按钮、一个停止按钮和一个文本字段。当按下开始按钮时,会产生一个新线程,该线程会不断增加计数器(在while循环中),文本字段会更新,以通过信号/插槽反映这一点。按下停止按钮时,计数停止,直到再次按下开始按钮 它是有效的。。。某种程度上。它每秒只更新计数器一次;我希望它跑得更快,比如说每秒44100次。但是,执行sleep(1)调用会导致while循环阻塞,无法识别GUI事件,并且应用程序会冻结。此外,停止按钮仅在第二次尝试时有效 当我对QThrea
class Worker : public QObject
{
Q_OBJECT
public:
Worker();
public slots:
void process();
void stopRunning();
signals:
void signalValueUpdated(QString);
private:
bool running;
};
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;
};
Worker.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;
}
#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
}
main window.h
class Worker : public QObject
{
Q_OBJECT
public:
Worker();
public slots:
void process();
void stopRunning();
signals:
void signalValueUpdated(QString);
private:
bool running;
};
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;
};
main window.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;
}
#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中实现多线程的最佳方法
QCoreApplication::processEvents()
(为什么需要它?GUI事件应该已经由主线程处理了…)。这可能是你问题的原因connect(workerThread, SIGNAL(started()), worker, SLOT(process()));
connect(startButton, SIGNAL(clicked()), thread, SLOT(start()));
(删除startButton.clicked()
->worker.process()
-它是无用的,不会执行注释中所写的操作)
为什么要删除它?
因为将按钮直接连接到辅助进程中的进程是不正确的。您应该将按钮连接到线程的开头,然后将线程(已启动)连接到辅助进程process()
。避免直接连接应该可以避免您面临的GUI冻结问题插槽
,然后从中创建线程(在main窗口
构造函数中创建线程将不起作用)
摘自我的(工作)计划:
// 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()
)
“停止”按钮也会给您带来一些问题,但您现在应该知道如何继续。您知道
QRunnable
?它表示一个工作项,然后可以通过QThreadPool::globalInstance()->start(runnable)
运行该工作项,而不必担心运行它的目标线程。只是一个替代方案的想法。启动单个QThread
实例更低级,QThreadPool
和QtConcurrent
更高级。另外,我想问一下,您为什么要使用Qt::QueuedConnection
?您对流程的要求太高了-在这样一个紧密的循环中更新GUI。它可能会崩溃,因为工作事件队列对于单个进程(例如2Gb)来说太大了—请观察进程的工作内存。尝试每秒更新GUI 44100次没有什么意义-没有人能读到它!我这样做了,现在一旦process()启动(while()循环运行),就不会接受任何GUI输入——单击Stop按钮什么也不做,调试消息“stopRunning()invoked”永远不会出现。将processEvents()放回解决了以下问题。。。再次编辑:删除一个连接,这样一旦按下开始键,然后按下停止键,再次按下开始键什么都不做……那么你做错了——你的代码正在GUI线程中运行@MartinJames是对的,如果用户输入不被接受,那么您正在主(GUI)线程中运行worker。从按钮到工作的连接应该是问题所在!这肯定可以解释,我该怎么解决这个问题呢?我认为通过调用worker->moveToThread(workerThread)可以避免,worker对象将被移动到一个单独的线程,其代码将在那里运行。编辑:另外,调试打印输出显示的线程id不同…@user2134278我认为问题出在连接中。。。我将编辑答案并从一些工作代码中提取一个片段。