Qt:工作线程和 GUI 事件之间的关系

Qt: The relation between Worker thread and GUI Events

本文关键字:事件 之间 关系 GUI 工作 线程 Qt      更新时间:2023-10-16

我有一个普通的GUI线程(主窗口),想将一个工作线程附加到它。Worker 线程将被实例化,移动到它自己的线程,然后触发以独立运行,运行消息传递例程(非阻塞)。

这是创建工作线程的位置:

void MainWindow::on_connectButton_clicked()
{
Worker* workwork;
workwork= new Worker();
connect(workwork,SIGNAL(invokeTestResultsUpdate(int,quint8)),
this,SLOT(updateTestResults(int,quint8)),Qt::QueuedConnection);
connect(this,SIGNAL(emitInit()),workwork,SLOT(init()));
workwork->startBC();
}

这是工作线程开始的地方:

void Worker::startBC()
{
t1553 = new QThread();    
this->moveToThread(t1553);
connect(t1553,SIGNAL(started()),this,SLOT(run1553Process()));
t1553->start();
}

关于新线程的事件队列,我在这里有两个问题:

第一个也是小问题是,虽然我可以接收来自 Worker 线程的信号(即:invokeTestResultsUpdate),但我无法通过从MainWindow发出emitInit信号来调用init方法。除非我直接调用它或通过Qt::DirectConnection连接它,否则它不会触发。为什么会这样?因为我必须显式启动工作线程自己的消息循环?还是其他我不知道的事情?(即使我尝试了,我真的无法理解线程/事件循环/信号槽机制的概念以及彼此之间的关系。我也欢迎任何新的视角。

第二个也是更晦涩的问题是:run1553process方法做一些繁重的工作。我所说的繁重工作是指非常高的数据率。有一个循环正在运行,我尝试在设备(实时)到达缓冲区后立即接收流出的数据,主要使用externAPI 函数。然后在每次收到消息时向 GUI 抛出上述invokeTestResultsUpdate信号,更新消息编号框。仅此而已。

我正在经历的事情很奇怪;通常消息传递例程基本上不受阻碍,但是当我调整主窗口的大小、移动它或隐藏/显示窗口时,Worker 线程会跳过许多消息。而且调整大小的操作真的很慢(响应不是很快)。这真的让我患上了癌症。

(注意:我之前尝试过对QThread进行子类化,但它并没有缓解这个问题。

我一直在阅读所有"线程亲和性"主题并尝试应用它们,但它的行为仍然像在某些时候被 GUI 线程的事件以某种方式打断。我可以理解 MainWindow 的麻烦,因为队列中有许多要执行的消息(调用的插槽和 GUI 事件)。但是我看不出为什么后台线程会受到 GUI 事件的影响。我真的需要一个非常强大且不受阻碍的消息例程,单独运行,发射并忘记信号,并且不在乎任何事情。

我现在真的很渴望任何帮助,所以任何一点信息对我都很有用。请不要犹豫,提出想法。

TL;DR:定期在run1553process内呼叫QCoreApplication::processEvents();

完整解释: 来自主线程的信号被放入队列中,并在第二个线程中的事件循环获得控制权后执行。在实现中,线程启动后立即调用run1553Process。在手动调用该函数或QCoreApplication::processEvents之前,控件不会返回到事件循环,因此信号将坐在那里等待事件循环拾取它们。

附言 您在上面的代码中泄漏了工作线程和线程

附言 来自设备的数据流通常提供异步 API,而不必无限期地轮询它们

我终于找到了问题所在。

关键错误是将QThread的内置信号连接到start()插槽run1553Process()。我认为这是用这种方法代替run(),并期望一切都好。但这会导致实际的run()方法被阻塞,从而阻止事件循环启动。

如qthread.cpp所述:

void QThread::run()
{
(void) exec();
}

为了解决这个问题,我没有触摸原始的start()信号,而是将另一个信号独立连接到我的run1553Process()。首先正常启动线程,允许事件循环启动,然后触发我的其他信号。做到了,现在我的 worker 可以接收所有消息。

我想现在我更好地理解了线程和事件之间的关系。

顺便说一下,这个解决方案并没有完全解决消息跳过问题,但我觉得这是由另一个因素引起的(比如我的消息阅读实现)。

谢谢大家的想法。我希望这个解决方案能帮助其他像我这样的穷人。