C++ 在Qt中使用多线程时的事件循环和信号槽处理
我在使用C++ 在Qt中使用多线程时的事件循环和信号槽处理,c++,multithreading,qt,event-loop,signals-slots,C++,Multithreading,Qt,Event Loop,Signals Slots,我在使用QThreads时遇到了一些问题,这使得我在找到正确的组合之前先研究了不同的组合。然而,当涉及到事件循环和信号槽处理时,我仍然不能完全理解下面所示的四种情况中到底发生了什么 我在“输出”部分添加了一些注释,但正如您所看到的,我不确定我关于导致观察到的行为的假设是否正确。另外,我不确定案例3是否可以在实际代码中使用。这是我的测试代码(只有main.cpp在每种情况下有所不同): 工人h: 输出-在a.exec()调用slot1和slot2(?-使用主事件循环?): main.cpp(案例2
QThreads
时遇到了一些问题,这使得我在找到正确的组合之前先研究了不同的组合。然而,当涉及到事件循环和信号槽处理时,我仍然不能完全理解下面所示的四种情况中到底发生了什么
我在“输出”部分添加了一些注释,但正如您所看到的,我不确定我关于导致观察到的行为的假设是否正确。另外,我不确定案例3
是否可以在实际代码中使用。这是我的测试代码(只有main.cpp
在每种情况下有所不同):
工人h:
输出-在a.exec()调用slot1
和slot2
(?-使用主事件循环?):
main.cpp(案例2-workerManager
移动到单独的线程,但线程未启动):
main.cpp(案例3-workerManager
移动到单独的线程,线程已启动,但通过workerManager->process()调用workerManager::process()
):
main.cpp(案例4-workerManager
移动到单独的线程,线程已启动,但使用start()
来自线程的信号调用workerManager::process()
):
感谢您的澄清。您得到的所有结果都是完全正确的。我将尝试解释这是如何工作的
事件循环是Qt代码中处理系统和用户事件的内部循环。调用a.exec()
时,主线程的事件循环启动。另一个线程的事件循环由QThread::run
的默认实现启动
当Qt决定处理事件的时间时,它执行其事件处理程序。当事件处理程序工作时,Qt没有机会处理任何其他事件(除非由QApplication::processEvents()
或其他一些方法直接给出)。一旦事件处理程序完成,控制流返回到事件循环,Qt可以执行另一个处理程序来处理另一个事件
信号和插槽与Qt术语中的事件和事件处理程序不同。但插槽由事件循环处理的方式有点类似。如果代码中有控制流(例如在<代码>主< /COD>函数),那么可以立即执行任何槽,就像任何其他C++函数一样。但当Qt这样做时,它只能从事件循环中完成。应该注意的是,信号总是立即发送,而时隙执行可能会延迟
现在让我们看看每种情况下会发生什么
案例1
WorkerManager::process
直接在程序启动时执行。启动新线程,并在新线程中立即执行Worker::process
WorkerManager::process
继续执行,直到Worker完成,冻结主线程中的所有其他操作(包括插槽处理)。在WorkerManager::process
完成后,控制流转到QApplication::exec
。Qt建立到另一个线程的连接,接收关于插槽调用的消息,并随后调用所有这些消息
案例2
默认情况下,Qt在该对象所属的线程中执行对象的插槽。主线程不会执行WorkerManager的插槽,因为它属于另一个线程。但是,此线程从未启动。它的事件循环永远不会结束。调用slot1
和slot2
将永远留在Qt的队列中,等待您启动线程。悲惨的故事
案例3
在这种情况下,WorkerManager::process
在主线程中执行,因为您直接从主线程调用它。同时,WorkerManager的线程启动。它的事件循环被启动并等待事件WorkerManager::process
启动Worker
的线程并在其中执行Worker::exec
<代码>工作者
开始向工作者管理者
发送信号WorkerManager的线程几乎立即开始执行适当的插槽。此时,同时执行WorkerManager::slot2
和WorkerManager::process
似乎有些尴尬。但至少在WorkerManager
是线程安全的情况下,这是非常好的。在Worker
完成后不久,WorkerManager::process
完成,执行a.exec()
,但没有太多要处理的内容
案例4
Main函数只需启动WorkerManager
的线程,并立即转到a.exec()
,从而将end
作为输出的第一行a.exec()
处理某些内容并确保程序执行,但不执行WorkerManager的插槽,因为它属于另一个线程WorkerManager::process
在其事件循环的WorkerManager
线程中执行Worker
的线程启动,Worker::process
开始从Worker
的线程向WorkerManager
的线程发送信号。不幸的是,后者正忙于执行WorkerManager::process
。当Worker
完成时,WorkerManager::process
也完成,并且WorkerManager
的线程立即执行所有排队的插槽
代码中最大的问题是usleep
和无限循环。在使用Qt时,几乎不应该使用这些。我知道在Worker::process
中睡眠只是一些实际计算的占位符。但您应该从WorkerManager
中删除睡眠和无限循环。使用WorkerManager::slot1
检测Worker
的终止。如果开发GUI应用程序,则无需将WorkerManager
移动到另一个线程。它的所有方法(无睡眠)都将快速执行,并且不会冻结GUI
#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_;
};
#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"; }
};
#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();
}
starting
Worker(0x112db20) processing started
Worker(0x112db20) processing finished
finished
end
slot2
slot2
slot2
slot2
slot2
slot1
#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();
}
starting
Worker(0x112db20) processing started
Worker(0x112db20) processing finished
finished
end
#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();
}
starting
Worker(0x197bb20) processing started
slot2
slot2
slot2
slot2
Worker(0x197bb20) processing finished
finished
end
slot2
slot1
#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();
}
end
starting
Worker(0x7f1d700013d0) processing started
Worker(0x7f1d700013d0) processing finished
finished
slot2
slot2
slot2
slot2
slot2
slot1