QThread::create 运行 在 UI 主题上
QThread::create running on UI Thread
我的应用程序需要多线程。根据 Qt 的文档,有多种方法可以实现这一点。
简要概述 QThread
ing 方法:
子类 QThread 并重新实现 运行() (doc).
使用 Q_OBJECT
宏(对于 signals/slots)使用 doWork
方法创建一个继承自 QObject
的对象,创建一个 QThread
对象,使用 QObject::moveToThread(QThread*) and call QThread::start()
(docs, wiki)
使用 QThread::create(Function &&f) 或 QThread::create(std::function<>)
作为 lambda 语法。
我选择使用第三个选项进行测试。我还实现了一个名为 QThreading based on option 2, using a QWorkerThread 的多线程库,它充当控制器对象,以 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();
}
输出为:
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 Thread in use
令我困惑的是不同的线程地址,但工作线程仍然等于 UI 线程。
这让我有两种解释:
QThread::currentThread
始终 returns 主机/主线程(难以置信,使函数有些无意义)
QThread *thread
位于 Main UI 线程 中,因此(获取父线程)总是 returns 父线程即 ParentThread == WorkerThread (ParentThread)
我想念 QThread
的工作原理吗?
- 如果你想知道它是否在主线程中运行,那么你应该将 currentThread 与 QCoreApplication 线程进行比较:
if(QCoreApplication::instance()->thread() == QThread::currentThread()) {
qDebug() << "UI Thread in use";
} else {
qDebug() << "Worker thread in use";
}
- QThread 不是线程,它是线程处理程序,正如 the docs 指出的那样:
Detailed Description
A QThread
object manages one thread of control within the program.
QThreads begin executing in run()
. By default, run() starts the event
loop by calling exec() and runs a Qt event loop inside the thread.
此外,QThread 是一个 QObject,它存在于父线程中,或者如果它没有父线程则创建它的地方,正如 the docs 指出的那样:
Thread Affinity
A QObject instance is said to have a thread affinity, or that it lives
in a certain thread. When a QObject receives a queued signal or a
posted event, the slot or event handler will run in the thread that
the object lives in.
Note: If a QObject has no thread affinity (that is, if thread()
returns zero), or if it lives in a thread that has no running event
loop, then it cannot receive queued signals or posted events.
By default, a QObject lives in the thread in which it is created. An
object's thread affinity can be queried using thread() and changed
using moveToThread().
All QObjects must live in the same thread as their parent.
Consequently:
- setParent() will fail if the two QObjects involved live in different threads.
- When a QObject is moved to another thread, all its children will be automatically moved too.
- moveToThread() will fail if the QObject has a parent.
- If QObjects are created within QThread::run(), they cannot become children of the QThread object because the QThread does not live in
the thread that calls QThread::run().
Note: A QObject's member variables do not automatically become
its children. The parent-child relationship must be set by either
passing a pointer to the child's constructor, or by calling
setParent(). Without this step, the object's member variables will
remain in the old thread when moveToThread() is called.
在你的例子中,"thread" 对象是一个存在于主线程中的 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();
}
输出:
<b>Worker thread in use</b>
[UI Thread 0x7fc6222993c0 ] 94
[Worker Thread 0x7fc621f62700 ] 71
[UI Thread 0x7fc6222993c0 ] 86
[Worker Thread 0x7fc621f62700 ] 94
[UI Thread 0x7fc6222993c0 ] 37
[Worker Thread 0x7fc621f62700 ] 86
...
我的应用程序需要多线程。根据 Qt 的文档,有多种方法可以实现这一点。
简要概述 QThread
ing 方法:
子类 QThread 并重新实现 运行() (doc).
使用
Q_OBJECT
宏(对于 signals/slots)使用doWork
方法创建一个继承自QObject
的对象,创建一个QThread
对象,使用 QObject::moveToThread(QThread*) and callQThread::start()
(docs, wiki)使用 QThread::create(Function &&f) 或
QThread::create(std::function<>)
作为 lambda 语法。
我选择使用第三个选项进行测试。我还实现了一个名为 QThreading based on option 2, using a QWorkerThread 的多线程库,它充当控制器对象,以 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();
}
输出为:
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 Thread in use
令我困惑的是不同的线程地址,但工作线程仍然等于 UI 线程。
这让我有两种解释:
QThread::currentThread
始终 returns 主机/主线程(难以置信,使函数有些无意义)QThread *thread
位于 Main UI 线程 中,因此(获取父线程)总是 returns 父线程即ParentThread == WorkerThread (ParentThread)
我想念 QThread
的工作原理吗?
- 如果你想知道它是否在主线程中运行,那么你应该将 currentThread 与 QCoreApplication 线程进行比较:
if(QCoreApplication::instance()->thread() == QThread::currentThread()) {
qDebug() << "UI Thread in use";
} else {
qDebug() << "Worker thread in use";
}
- QThread 不是线程,它是线程处理程序,正如 the docs 指出的那样:
Detailed Description
A
QThread
object manages one thread of control within the program. QThreads begin executing inrun()
. By default, run() starts the event loop by calling exec() and runs a Qt event loop inside the thread.
此外,QThread 是一个 QObject,它存在于父线程中,或者如果它没有父线程则创建它的地方,正如 the docs 指出的那样:
Thread Affinity
A QObject instance is said to have a thread affinity, or that it lives in a certain thread. When a QObject receives a queued signal or a posted event, the slot or event handler will run in the thread that the object lives in.
Note: If a QObject has no thread affinity (that is, if thread() returns zero), or if it lives in a thread that has no running event loop, then it cannot receive queued signals or posted events.
By default, a QObject lives in the thread in which it is created. An object's thread affinity can be queried using thread() and changed using moveToThread().
All QObjects must live in the same thread as their parent. Consequently:
- setParent() will fail if the two QObjects involved live in different threads.
- When a QObject is moved to another thread, all its children will be automatically moved too.
- moveToThread() will fail if the QObject has a parent.
- If QObjects are created within QThread::run(), they cannot become children of the QThread object because the QThread does not live in the thread that calls QThread::run().
Note: A QObject's member variables do not automatically become its children. The parent-child relationship must be set by either passing a pointer to the child's constructor, or by calling setParent(). Without this step, the object's member variables will remain in the old thread when moveToThread() is called.
在你的例子中,"thread" 对象是一个存在于主线程中的 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();
}
输出:
<b>Worker thread in use</b>
[UI Thread 0x7fc6222993c0 ] 94
[Worker Thread 0x7fc621f62700 ] 71
[UI Thread 0x7fc6222993c0 ] 86
[Worker Thread 0x7fc621f62700 ] 94
[UI Thread 0x7fc6222993c0 ] 37
[Worker Thread 0x7fc621f62700 ] 86
...