在我的Qt应用程序中,我创建了一个QThread,它应该定期执行一些繁重的计算任务。主QApplication线程应该维护GUI(不包括在示例中)并定期执行一些更新操作。两个线程都有自己的计时器以启用定期的update()调用。
问题是:当工作线程的计算工作量超过某个临界值时,我的主线程停止接收计时器事件。
以下是示例代码。它会输出“Main”,表示主线程的update()方法被调用;输出“Worker”,表示工作线程的update()方法被调用。如果您运行它,您会看到“Worker”被定期打印,“Main”则只出现两次(一个在开头,一个大约在5秒后)。在完整的GUI应用程序中,这实际上意味着GUI完全冻结。
一些观察:
1. 在内部循环上设置100的限制(而不是1000)将解决问题(两个update()方法都将被定期调用)。 2. 将工作线程计时器信号的连接类型设置为Qt::DirectConnection可以解决问题。
因此,正如您所看到的,我有几种解决方法,但我希望有人向我解释原始代码存在的问题。我期望线程独立地执行它们的事件循环。我知道我通过长时间的update()操作来阻塞工作线程事件循环,但为什么会影响主线程呢?
顺便说一句,是的,我知道有QConcurrent这个替代方案。但我只是想了解一下。
test.cpp
问题是:当工作线程的计算工作量超过某个临界值时,我的主线程停止接收计时器事件。
以下是示例代码。它会输出“Main”,表示主线程的update()方法被调用;输出“Worker”,表示工作线程的update()方法被调用。如果您运行它,您会看到“Worker”被定期打印,“Main”则只出现两次(一个在开头,一个大约在5秒后)。在完整的GUI应用程序中,这实际上意味着GUI完全冻结。
一些观察:
1. 在内部循环上设置100的限制(而不是1000)将解决问题(两个update()方法都将被定期调用)。 2. 将工作线程计时器信号的连接类型设置为Qt::DirectConnection可以解决问题。
因此,正如您所看到的,我有几种解决方法,但我希望有人向我解释原始代码存在的问题。我期望线程独立地执行它们的事件循环。我知道我通过长时间的update()操作来阻塞工作线程事件循环,但为什么会影响主线程呢?
顺便说一句,是的,我知道有QConcurrent这个替代方案。但我只是想了解一下。
test.cpp
#include <windows.h>
#include <QApplication>
#include "test.h"
HANDLE mainThread_ = INVALID_HANDLE_VALUE;
QApplication *app_ = 0;
MyObj *obj_ = 0;
MyThread *thread_ = 0;
MyObj::MyObj()
: timer_(0)
{
timer_ = new QTimer(0);
connect(timer_, SIGNAL(timeout()), this, SLOT(update()));
timer_->start(10);
}
void MyObj::update()
{
printf("Main\n");
}
void MyThread::run()
{
timer_ = new QTimer(0);
connect(timer_, SIGNAL(timeout()), this, SLOT(update()));
timer_->start(10);
exec();
}
void MyThread::update()
{
printf("Worker\n");
// do some hard work
float f = 0.f;
for (int i=0; i < 100000; ++i)
{
for (int j=0; j < 1000; ++j)
{
f += i * j;
}
}
}
int main()
{
int argc = 0;
app_ = new QApplication(argc, 0);
obj_ = new MyObj();
thread_ = new MyThread();
thread_->start();
QApplication::exec();
return 0;
}
test.h
#include <QTimer>
#include <QThread>
class MyObj : public QObject
{
Q_OBJECT
public:
MyObj();
public slots:
void update();
private:
QTimer *timer_;
};
class MyThread : public QThread
{
Q_OBJECT
public:
void run();
public slots:
void update();
private:
QTimer *timer_;
};
更新:我收到了一些来自尊敬的成员的答案(请在下面阅读)。现在我想澄清哪个错误的想法破坏了我的代码。
如您所见,计划是拥有两个线程,每个线程定期运行一些update()过程。我的错误是认为update()只是一个过程,而它是一个槽。特定对象的槽具有其自己的线程关联,这意味着其主体将在该线程中执行(除非使用Qt :: DirectConnection分派信号)。现在,看起来我已经用计时器做得很好——它们中的每一个都属于不同的线程——但在update()方面搞砸了。因此,我最终在主线程中执行了两个update()程序。显然,在某些时候,事件循环会被计时器事件淹没,并且永远不会完成迭代。
至于解决方案。如果您已经阅读了“你错了”(确实应该),则知道在未从QThread子类化而是单独创建并附加到QThread的对象中实现所有逻辑相当方便。就个人而言,我认为从QThread子类化并没有什么错误,但请记住,您的对象仅控制���程而不属于该线程。因此,这不是您希望在该线程中执行的代码的位置。
在QThread子类中实现新的槽函数容易出错且不被鼓励。
- thuga