[英]Running a long operation in QThread and sending signals to the main thread still freezes the UI
[英]QThread::create running on UI Thread
我的应用程序需要多线程。 根据 Qt 的文档,有很多方法可以实现这一点。
简要概述QThread
ing方法:
子类 QThread 并重新实现 run() ( doc )。
创建一个 object 继承自QObject
并使用Q_OBJECT
宏(用于信号/插槽)和doWork
方法,创建一个QThread
object,使用QObject::moveToThread(QThread*)并调用QThread::start()
(文档,维基)
对 lambda 语法使用QThread::create(Function &&f)或QThread::create(std::function<>)
。
我选择使用第三个选项进行测试。 我还基于选项 2 实现了一个名为QThreading的多线程库,使用QWorkerThread充当 controller object, QThread
和QObject
作为对象。 这个库也给出了相同的结果,如下所示。
既然文档已经不在了,问题就来了。
使用QThread::create(Function &&f)
,我正在测试 QThread 是否与 UI 线程分开运行。 下面的MCVE示例:
#include <QCoreApplication>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qsrand(0);
QThread *thread = nullptr;
auto threadTest = std::function<void ()> ([&]() {
if(thread->thread() == a.thread()) {
qDebug() << "UI Thread in use";
} else {
qDebug() << "Worker thread in use";
}
for (int var = 0; var < INT_MAX; ++var) {
int r = qrand() % 100;
thread->thread()->msleep(r);
qDebug() << "[Worker Thread " << thread->thread()->currentThreadId() << "] " << r;
}
});
thread = QThread::create(threadTest);
thread->start();
for (int var = 0; var < INT_MAX; ++var) {
// sleep thread 0-100ms
int r = qrand() % 100;
a.thread()->msleep(r);
qDebug() << "[UI Thread " << a.thread()->currentThreadId() << "] " << r;
}
return a.exec();
}
output 是:
UI Thread in use
[Worker Thread 0x47e4 ] 41
[UI Thread 0x10c0 ] 38
[UI Thread 0x10c0 ] 19
[UI Thread 0x10c0 ] 38
[Worker Thread 0x47e4 ] 67
[UI Thread 0x10c0 ] 37
[Worker Thread 0x47e4 ] 34
[Worker Thread 0x47e4 ] 0
[UI Thread 0x10c0 ] 55
[Worker Thread 0x47e4 ] 69
[Worker Thread 0x47e4 ] 24
[UI Thread 0x10c0 ] 97
[Worker Thread 0x47e4 ] 78
[UI Thread 0x10c0 ] 65
[Worker Thread 0x47e4 ] 58
[UI Thread 0x10c0 ] 85
[Worker Thread 0x47e4 ] 62
[UI Thread 0x10c0 ] 50
[Worker Thread 0x47e4 ] 64
[UI Thread 0x10c0 ] 12
[Worker Thread 0x47e4 ] 5
[Worker Thread 0x47e4 ] 45
注意事项
UI 线程 ID: 0x10c0
工作线程 ID: 0x47e4
担忧来自哪里
正在使用的 UI 线程
让我困惑的是不同的线程地址,但工作线程仍然等于UI 线程。
这让我有两种解释:
QThread::currentThread
总是返回主机/主线程(难以置信,使得 function 有点毫无意义)
QThread *thread
位于主 UI 线程内,因此(获取父线程)始终返回父线程,即ParentThread == WorkerThread (ParentThread)
我是否想了解QThread
的工作原理?
if(QCoreApplication::instance()->thread() == QThread::currentThread()) {
qDebug() << "UI Thread in use";
} else {
qDebug() << "Worker thread in use";
}
详细说明
QThread
object 管理程序中的一个控制线程。 QThreads 在run()
中开始执行。 默认情况下,run() 通过调用 exec() 启动事件循环,并在线程内运行 Qt 事件循环。
此外,QThread 是一个 QObject ,它存在于父线程中,或者如果它没有父线程,则它被创建的地方,正如文档指出的那样:
线程亲和力
QObject 实例被称为具有线程亲和性,或者说它存在于某个线程中。 当 QObject 接收到排队的信号或发布的事件时,槽或事件处理程序将在 object 所在的线程中运行。
注意:如果 QObject 没有线程关联(即,如果 thread() 返回零),或者如果它存在于没有运行事件循环的线程中,则它无法接收排队信号或发布的事件。
默认情况下,QObject 存在于创建它的线程中。 可以使用 thread() 查询对象的线程亲和性,并使用 moveToThread() 更改对象的线程亲和性。
所有 QObject 必须与其父对象存在于同一线程中。 最后:
- 如果涉及的两个 QObject 存在于不同的线程中,setParent() 将失败。
- 当一个 QObject 被移动到另一个线程时,它的所有子对象也将被自动移动。
- 如果 QObject 有父对象,则 moveToThread() 将失败。
- 如果 QObjects 在 QThread::run() 中创建,它们不能成为 QThread object 的子级,因为 QThread 不存在于调用 QThread::run() 的线程中。
注意: QObject 的成员变量不会自动成为它的子变量。 必须通过将指针传递给子构造函数或调用 setParent() 来设置父子关系。 如果没有这一步,调用 moveToThread() 时对象的成员变量将保留在旧线程中。
在您的情况下,“线程” object 是一个 QObject ,它存在于主线程中,因为它没有父线程并且在那里创建并处理另一个线程。
MWE
#include <QCoreApplication>
#include <QDebug>
#include <QThread>
#include <QTimer>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qsrand(0);
auto threadTest = std::function<void ()> ([&]() {
if(QCoreApplication::instance()->thread() == QThread::currentThread()) {
qDebug() << "UI Thread in use";
} else {
qDebug() << "Worker thread in use";
}
for (int var = 0; var < INT_MAX; ++var) {
int r = qrand() % 100;
QThread::msleep(r);
qDebug() << "[Worker Thread " << QThread::currentThreadId() << "] " << r;
}
});
QThread *thread = QThread::create(threadTest);
thread->start();
int var = 0;
std::function<void ()> timerTest;
timerTest = [&](){
int r = qrand() % 100;
qDebug() << "[UI Thread " << QThread::currentThreadId() << "] " << r;
++var;
if (var < INT_MAX)
QTimer::singleShot(r, timerTest);
};
int r = qrand() % 100;
QTimer::singleShot(r, timerTest);
return a.exec();
}
Output:
Worker thread in use
[UI Thread 0x7fc6222993c0 ] 94
[Worker Thread 0x7fc621f62700 ] 71
[UI Thread 0x7fc6222993c0 ] 86
[Worker Thread 0x7fc621f62700 ] 94
[UI Thread 0x7fc6222993c0 ] 37
[Worker Thread 0x7fc621f62700 ] 86
...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.