在我的 Qt 应用程序中,我创建了一个QThread
应该定期执行一些繁重的计算任务。主QApplication
线程应该维护一个 GUI(不包括在示例中)并执行一些定期更新。两个线程都有自己的计时器来启用定期 update() 调用。
问题:当工作线程的计算工作量超过某个临界值时,我的主线程停止接收定时器事件。
示例代码如下。主线程调用 update() 时输出“Main”,工作线程调用“Worker”。如果您运行它,您会看到定期打印“Worker”,并且“Main”恰好出现两次(开始时出现一次,大约 5 秒后出现一次)。如果是功能齐全的 GUI 应用程序,这实际上意味着 GUI 完全冻结。
一些观察。
- 通过对内部循环设置 100 个限制(而不是 1000 个)来减少工作量将解决问题(两个 update() 方法都将被定期调用)。
- 将工作线程计时器信号的连接类型设置为 Qt::DirectConnection 将解决该问题。
因此,正如您所看到的,我对此有几个解决方法,但如果有人向我解释原始代码有什么问题,我将不胜感激。我希望线程独立执行它们的事件循环。我知道我通过长时间的 update() 操作阻塞了工作线程事件循环,但是为什么它会影响主线程呢?
PS 是的,我知道QConcurrent
替代方案。但我只是想了解一下。
测试.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;
}
测试.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_;
};
UPD:我从受人尊敬的成员那里得到了一些答案(请阅读下面的内容)。现在我想澄清一下是什么错误的想法特别破坏了我的代码。
如您所见,该计划有两个线程,每个线程都定期运行一些 update() 过程。我的错误是认为 update() 只是一些过程,它是一个slot。一个特定对象的槽,它有自己的线程亲和性,这意味着它的主体将在该线程中执行(除非使用 Qt::DirectConnection 调度信号)。现在,看来我已经用计时器做得很好了——它们中的每一个都属于不同的线程——但是用 update() 把事情搞砸了。所以我最终在主线程中执行了两个 update() 过程。显然在某些时候事件循环会被定时器事件淹没,并且永远不会完成迭代。
至于解决办法。如果你读过“你做错了”(你确实应该),你就会知道将所有逻辑实现在一个不是 QThread 子类但单独创建并使用 moveToThread 附加到 QThread 的对象中是相当方便的( )。如果您记住您的对象仅控制线程但不属于它,我个人认为从 QThread 子类化没有任何问题。所以它不是你想在那个线程中执行的代码的地方。