Multithreading QApplication线程因另一个QThread而冻结
在我的Qt应用程序中,我创建了一个Multithreading QApplication线程因另一个QThread而冻结,multithreading,qt,qthread,qtcore,Multithreading,Qt,Qthread,Qtcore,在我的Qt应用程序中,我创建了一个QThread,它应该定期执行一些繁重的计算任务。MainQApplication线程应该维护一个GUI(不包括在示例中),并执行一些定期更新。两个线程都有自己的计时器来启用常规update()调用 问题:当工作线程的计算负载超过某个临界值时,我的主线程停止接收计时器事件 下面是示例代码。当对主线程调用update()时,它输出“Main”,对工作线程则输出“Worker”。如果您运行它,您将看到“Worker”定期打印,“Main”正好出现两次(一次在开头,一
QThread
,它应该定期执行一些繁重的计算任务。MainQApplication
线程应该维护一个GUI(不包括在示例中),并执行一些定期更新。两个线程都有自己的计时器来启用常规update()调用
问题:当工作线程的计算负载超过某个临界值时,我的主线程停止接收计时器事件
下面是示例代码。当对主线程调用update()时,它输出“Main”,对工作线程则输出“Worker”。如果您运行它,您将看到“Worker”定期打印,“Main”正好出现两次(一次在开头,一次在大约5秒钟内)。对于功能齐全的GUI应用程序,这实际上意味着完全冻结GUI
一些观察
QConcurrent
备选方案。但我只是想理解
test.cpp
#包括
#包括
#包括“test.h”
句柄主线程=无效的句柄值;
QApplication*app_u2;=0;
MyObj*obj_j=0;
MyThread*线程=0;
MyObj::MyObj()
:计时器(0)
{
计时器=新的QTimer(0);
连接(计时器、信号(超时())、此、插槽(更新());
定时器->启动(10);
}
void MyObj::update()
{
printf(“主\n”);
}
void MyThread::run()
{
计时器=新的QTimer(0);
连接(计时器、信号(超时())、此、插槽(更新());
定时器->启动(10);
exec();
}
void MyThread::update()
{
printf(“Worker\n”);
//做一些艰苦的工作
浮点数f=0.f;
对于(int i=0;i<100000;++i)
{
对于(int j=0;j<1000;++j)
{
f+=i*j;
}
}
}
int main()
{
int argc=0;
app=新的QApplication(argc,0);
obj_uj=新的MyObj();
线程=新的MyThread();
线程->开始();
QApplication::exec();
返回0;
}
测试h
#包括
#包括
MyObject类:公共QObject
{
Q_对象
公众:
MyObj();
公众时段:
无效更新();
私人:
Q定时器*定时器;
};
类MyThread:publicqthread
{
Q_对象
公众:
无效运行();
公众时段:
无效更新();
私人:
Q定时器*定时器;
};
UPD:我从尊敬的会员那里得到了一些答案(请阅读下文)。现在我想澄清是什么错误的想法破坏了我的代码
正如您所见,计划中有两个线程,每个线程定期运行一些update()过程。我的错误是认为update()只是一个过程,它是一个插槽。特定对象的一个插槽,它有自己的线程亲缘关系,这意味着它的主体将在该线程中执行(除非用Qt::DirectConnection发送信号)。现在,我似乎已经用定时器做得很好了——每个定时器都属于不同的线程——但update()却把事情搞砸了。因此,我最终在主线程中执行了两个update()过程。显然,在某个时刻,事件循环被计时器事件淹没,并且永远不会完成迭代
至于解决办法。如果您已经读过(并且确实应该读过),您就会知道,在一个对象中实现所有逻辑非常方便,该对象不是从QThread子类化的,而是单独创建的,并使用moveToThread()附加到QThread。就我个人而言,如果您记住您的对象只控制线程而不属于它,那么从QThread生成子类并没有什么错。因此,它不是您希望在该线程中执行代码的位置。首先
从你的主页上,我看不到任何东西像GUI
。。。您只需调用QApplication::exec()
,无论出于何种原因,而不是app->exec()
(?)
对于线程问题:
您可以创建一个QObject派生类,该类具有插槽doUpdate()
左右。有了它,您可以像这样做:
TheUpdateObject* obj = new TheUpdateObject;
obj->moveToThread(thread_); // thread_ is a QThread object
thread_->start();
connect(thread_, SIGNAL(finished()), obj, SLOT(deleteLater()));
QTimer* tmr = new QTimer(this);
tmr->setTimeout(10);
connect(tmr, SIGNAL(timeout()), obj, SLOT(doUpdate()));
connect(tmr, SIGNAL(timeout()), tmr, SLOT(start()));
tmr->start();
因此,计时器应该自行重新启动,并且应该在另一个线程中调用doUpdate()
在GUI内部,您不需要检查更新,qt框架应该在需要时重新绘制。这里的第一个问题是您继承了QThread,因此它声明 您遇到的问题源于线程关联(对象运行在哪个线程上)。例如,如果要从QThread继承并在构造函数中创建对象,而不作为对象的子对象,则该对象将在主线程中运行,而不是在新线程中运行。因此,在MyThread构造函数中,您将有:-
MyThread::MyThread()
: timer_(0)
{
timer_ = new QTimer(0);
connect(timer_, SIGNAL(timeout()), this, SLOT(update()));
timer_->start(10);
}
这里的计时器(timer_)将在主线程上运行,而不是在新线程上运行。
为了避免重复我自己,我先前的一个答案解释道
解决此问题的最佳方法是将类更改为从QObject继承,然后将该对象移动到新线程。谢谢。事实上,我已经读了很多类似“你做错了”这样的帖子,但直到现在我才开始明白。所以MyThread对象驻留在主线程中,发送给它的计时器事件进入主线程的事件循环。酷。现在,我想知道为什么主线程的update()几乎从来没有机会?我的意思是,两个计时器的间隔相等,它们应该是头对头的。您可以在不同的点(例如构造函数、运行函数和更新函数)从MyObject和MyThread打印线程的地址。这会给你带来快乐
TheUpdateObject* obj = new TheUpdateObject;
obj->moveToThread(thread_); // thread_ is a QThread object
thread_->start();
connect(thread_, SIGNAL(finished()), obj, SLOT(deleteLater()));
QTimer* tmr = new QTimer(this);
tmr->setTimeout(10);
connect(tmr, SIGNAL(timeout()), obj, SLOT(doUpdate()));
connect(tmr, SIGNAL(timeout()), tmr, SLOT(start()));
tmr->start();
MyThread::MyThread()
: timer_(0)
{
timer_ = new QTimer(0);
connect(timer_, SIGNAL(timeout()), this, SLOT(update()));
timer_->start(10);
}