gpt4 book ai didi

c++ - 在 Qt 中使用多线程时的事件循环和信号槽处理

转载 作者:可可西里 更新时间:2023-11-01 15:05:29 25 4
gpt4 key购买 nike

我在使用 QThreads 时遇到了一些问题这让我在找到合适的组合之前探索了不同的组合。然而,当涉及到事件循环和信号槽处理时,我仍然不完全理解在下面显示的四种情况下真正发生了什么。

我在 OUTPUT 部分添加了一些注释,但正如您所看到的,我不确定我对导致观察到的行为的原因的假设是否正确。另外我不确定 case 3是可能在实际代码中使用的东西。这是我的测试代码(只有 main.cpp 因每种情况而异):

worker.h:


#include <QObject>
#include <QDebug>
#include <QThread>

class Worker : public QObject
{
Q_OBJECT
public:
explicit Worker(QObject *parent = 0) { this->isRunning_ = false;}
bool isRunning() const { return isRunning_; }

signals:
void processingFinished();
void inProgress();

public slots:
void process()
{
this->isRunning_ = true;
qDebug() << this << "processing started";
for (int i = 0; i < 5; i++)
{
QThread::usleep(1000);
emit this->inProgress();
}
qDebug() << this << "processing finished";
this->isRunning_ = false;
emit this->processingFinished();
}

private:
bool isRunning_;
};

workermanager.h:


#include "worker.h"

class WorkerManager : public QObject
{
Q_OBJECT
public:
explicit WorkerManager(QObject *parent = 0) :
QObject(parent) {}

public slots:
void process()
{
QThread *thread = new QThread();
Worker *worker = new Worker();

connect(thread,SIGNAL(started()),worker,SLOT(process()));
connect(worker,SIGNAL(processingFinished()),this,SLOT(slot1()));
connect(worker,SIGNAL(inProgress()),this,SLOT(slot2()));
worker->moveToThread(thread);

qDebug() << "starting";
thread->start();
QThread::usleep(500);
while(worker->isRunning()) { }
qDebug() << "finished";
}

void slot1() { qDebug() << "slot1"; }
void slot2() { qDebug() << "slot2"; }
};

main.cpp (case 1 - no separate thread for workerManager):


#include <QCoreApplication>
#include "workermanager.h"

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

WorkerManager* workerManager = new WorkerManager;
workerManager->process();
qDebug() << "end";
return a.exec();
}

OUTPUT - both slot1 and slot2 called at a.exec() (??? - using main event loop?):


starting 
Worker(0x112db20) processing started
Worker(0x112db20) processing finished
finished
end
slot2
slot2
slot2
slot2
slot2
slot1

main.cpp (case 2 - workerManager moved to separate thread, but thread not started):


#include <QCoreApplication>
#include "workermanager.h"

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

WorkerManager* workerManager = new WorkerManager;
QThread *thread = new QThread();
workerManager->moveToThread(thread);
workerManager->process();
qDebug() << "end";
return a.exec();
}

OUTPUT - neither slot1 nor slot2 was called - (??? event loop associated with thread receives signals but since thread was not started slots are not called?):


starting 
Worker(0x112db20) processing started
Worker(0x112db20) processing finished
finished
end

main.cpp (case 3 - workerManager moved to separate thread, thread started but workerManager::process() called via workerManager->process()):


#include <QCoreApplication>
#include "workermanager.h"

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

WorkerManager* workerManager = new WorkerManager;
QThread *thread = new QThread();
workerManager->moveToThread(thread);
thread->start();
workerManager->process();
qDebug() << "end";
return a.exec();
}

OUTPUT - slot2 called while Worker still executing its process() (???):


starting 
Worker(0x197bb20) processing started
slot2
slot2
slot2
slot2
Worker(0x197bb20) processing finished
finished
end
slot2
slot1

main.cpp (case 4 - workerManager moved to separate thread, thread started but workerManager::process() called using started() signal from thread):


#include <QCoreApplication>
#include "workermanager.h"

int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);

WorkerManager* workerManager = new WorkerManager;
QThread *thread = new QThread();
workerManager->moveToThread(thread);
QObject::connect(thread,SIGNAL(started()),workerManager,SLOT(process()));
thread->start();
qDebug() << "end";
return a.exec();
}

OUTPUT - all events processed after reaching a.exec() (???):


end 
starting
Worker(0x7f1d700013d0) processing started
Worker(0x7f1d700013d0) processing finished
finished
slot2
slot2
slot2
slot2
slot2
slot1

感谢您的任何澄清。

最佳答案

你得到的所有结果都是完全正确的。我将尝试解释这是如何工作的。

事件循环是 Qt 代码中处理系统和用户事件的内部循环。当您调用 a.exec() 时,主线程的事件循环开始.另一个线程的事件循环默认启动QThread::run的实现.

当 Qt 决定是时候处理一个事件时,它会执行它的事件处理程序。当事件处理程序工作时,Qt 没有机会处理任何其他事件(除非由 QApplication::processEvents() 或其他一些方法直接给出)。一旦事件处理程序完成,控制流返回到事件循环,Qt 可能会执行另一个处理程序来处理另一个事件。

信号和槽与 Qt 术语中的事件和事件处理程序不同。但是插槽由事件循环处理有点类似。如果您的代码中有控制流(例如在 main 函数中),您可以像任何其他 C++ 函数一样立即执行任何插槽。但是当 Qt 这样做时,它只能从事件循环中做到这一点。需要注意的是,信号总是立即发送,而槽执行可能会延迟。

现在让我们看看在每种情况下会发生什么。

情况1
WorkerManager::process在程序开始时直接执行。新线程开始,Worker::process在新线程中立即执行。 WorkerManager::process继续执行直到 Worker 完成,卡住主线程中的所有其他操作(包括槽处理)。后 WorkerManager::process完成,控制流转到 QApplication::exec . Qt 建立到另一个线程的连接,接收有关插槽调用的消息并因此调用所有这些消息。

案例二

Qt 默认在该对象所属的线程中执行该对象的槽。主线程不会执行 WorkerManager 的槽因为它属于另一个线程。然而,这个线程永远不会启动。它的事件循环永远不会完成。 slot1 的调用和 slot2永远留在 Qt 的队列中等待您启动线程。悲剧。

案例3

在这种情况下 WorkerManager::process在主线程中执行,因为您直接从主线程调用它。同时,WorkerManager的线程启动。它的事件循环启动并等待事件。 WorkerManager::process开始 Worker的线程并执行 Worker::exec在里面。 Worker开始向 WorkerManager 发送信号. WorkerManager的线程几乎立即开始执行适当的插槽。在这一点上似乎很尴尬WorkerManager::slot2WorkerManager::process是同时执行的。但它完全没问题,至少如果 WorkerManager是线程安全的。不久之后 Worker完成,WorkerManager::process完成并a.exec()已执行但没有太多处理。

案例四

主要功能刚刚启动 WorkerManager的线程并立即转到 a.exec() ,导致 end作为输出中的第一行。 a.exec()处理某些东西并确保程序执行但不执行 WorkerManager的插槽,因为它属于另一个线程。 WorkerManager::processWorkerManager 中执行的事件循环中的线程。 Worker的线程启动并且 Worker::process开始从 Worker 发送信号的线程到 WorkerManager的线程。不幸的是后者正忙于执行 WorkerManager::process .当Worker完成,WorkerManager::process也完成和 WorkerManager的线程立即执行所有排队的插槽。

您代码中最大的问题是 usleep和无限循环。在使用 Qt 时,您几乎不应该使用它们。据我所知,在 Worker::process 中 sleep 只是一些实际计算的占位符。但是你应该从 WorkerManager 中删除 sleep 和无限循环.使用 WorkerManager::slot1检测 Worker的终止。如果您开发 GUI 应用程序,则无需移动 WorkerManager到另一个线程。它的所有方法(没有 sleep )将快速执行并且不会卡住 GUI。

关于c++ - 在 Qt 中使用多线程时的事件循环和信号槽处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28055749/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com