I need multi-threading in my application. According to Qt's docs, there are a number of ways to achieve this.
To briefly overview QThread
ing methods:
subclass QThread and reimplement run() (doc).
Create a object inheriting from QObject
with Q_OBJECT
macro (for signals/slots) with doWork
method, create a QThread
object, use QObject::moveToThread(QThread*) and call QThread::start()
(docs, wiki)
use QThread::create(Function &&f) or QThread::create(std::function<>)
for lambda syntax.
I have opted to test using the 3rd option. I have also implemented a multithreading library called QThreading based on option 2, using a QWorkerThread which acts as a controller object with a QThread
and QObject
as objects. This library also gives the same results as shown below.
Now that the docs are out of the way, the question.
Using QThread::create(Function &&f)
, I am testing to see if the QThread runs separately from the UI thread. A MCVE example below:
#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();
}
The output is:
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
Things to note
UI Thread ID : 0x10c0
Worker Thread ID: 0x47e4
Where the concern comes in
UI Thread in use
What confuses me is the different thread addresses, yet the worker thread is still equal to the UI thread.
That leaves me to 2 explanations:
QThread::currentThread
always returns the host / main thread (implausible, makes the function somewhat pointless)
The QThread *thread
lives inside of the Main UI Thread and thus (getting the parent thread) always returns the parent thread i.e. ParentThread == WorkerThread (ParentThread)
Do I miss understand how the QThread
works?
if(QCoreApplication::instance()->thread() == QThread::currentThread()) {
qDebug() << "UI Thread in use";
} else {
qDebug() << "Worker thread in use";
}
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.
In addition, QThread is a QObject that lives in the parent's thread or where it was created if it has no parent as the docs points out:
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.
In your case "thread" object is a QObject that lives in the main thread since it has no parent and there it was created and that handles another thread.
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
...