qt thread with movetothread
The canonical Qt way would look like this:
QThread* thread = new QThread( );
Task* task = new Task();
// move the task object to the thread BEFORE connecting any signal/slots
task->moveToThread(thread);
connect(thread, SIGNAL(started()), task, SLOT(doWork()));
connect(task, SIGNAL(workFinished()), thread, SLOT(quit()));
// automatically delete thread and task object when work is done:
connect(task, SIGNAL(workFinished()), task, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
in case you arent familiar with signals/slots, the Task class would look something like this:
class Task : public QObject
{
Q_OBJECT
public:
Task();
~Task();
public slots:
// doWork must emit workFinished when it is done.
void doWork();
signals:
void workFinished();
};
moveToThread vs deriving from QThread in Qt
I would focus on the differences between the two methods. There isn't a general answer that fits all use cases, so it's good to understand exactly what they are to choose the best that fits your case.
Using moveToThread()
moveToThread() is used to control the object's thread affinity, which basically means setting the thread (or better the Qt event loop) from which the object will emit signals and its slots will be executed.
As shown in the documentation you linked, this can be used to run code on a different thread, basically creating a dummy worker, writing the code to run in a public slot (in the example the doWork() slot) and then using moveToThread to move it to a different event loop.
Then, a signal connected to that slot is fired. Since the object that emits the signal (the Controller in the example) lives in a different thread, and the signal is connected to our doWork method with a queued connection, the doWork method will be executed in the worker thread.
The key here is that you are creating a new event loop, run by the worker thread. Hence, once the doWork slot has started, the whole event loop will be busy until it exits, and this means that incoming signals will be queued.
Subclassing QThread()
The other method described in Qt's documentation is subclassing QThread. In this case, one overrides the default implementation of the QThread::run() method, which creates an event loop, to run something else.
There's nothing wrong with this approach itself, although there are several catches.
First of all, it is very easy to write unsafe code, because the run() method is the only one in that class that will be actually run on another thread.
If as an example, you have a member variable that you initialize in the constructor and then use in the run() method, your member is initialized in the thread of the caller and then used in the new thread.
Same story for any public method that could be called either from the caller or inside run().
Also slots would be executed from the caller's thread, (unless you do something really weird as moveToThread(this)) leading to extra confusion.
So, it is possible, but you really are on your own with this approach and you must pay extra attention.
Other approaches
There are of course alternatives to both approaches, depending on what you need. If you just need to run some code in background while your GUI thread is running you may consider using QtConcurrent::run().
However, keep in mind that QtConcurrent will use the global QThreadPool. If the whole pool is busy (meaning there aren't available threads in the pool), your code will not run immediately.
Another alternative, if you are at the least on C++11, is to use a lower level API such as std::thread.
Qt thread ID is equal to MainWindows? (moveToThread)
There are several issues that I'll address in random order.
First of all, using thread IDs is bad user experience. Give the threads a descriptive name:
int main(...) {
QApplication app(...);
QThread myThread;
MyObject myObject;
myObject->moveToThread(&myThread);
QThread::currentThread()->setObjectName("mainThread");
myThread.setObjectName("myThread");
...
}
Then use QThread::currentThread()->objectName()
to retrieve it. You can also pass QObject*
to qDebug()
to display the name of the thread:
qDebug() << QThread::currentThread();
Your signal invocation would then become:
QString currentThreadName() {
return QThread::currentThread()->objectName().isEmpty() ?
QStringLiteral("0x%1").arg(QThread::currentThread(), 0, 16) :
QThread::currentThread()->objectName();
}
...
emit requestFileContent(
QStringLiteral("Emitting from thread \"%1\"\n").arg(currentThreadName));
Then, use the above to deal with the thread you've created:
auto thread = new QThread(this);
thread->setObjectName("fileThread");
ui.textBrowser->append(QStringLiteral("Worker thread: \"%1\").arg(thread->objectName()));
auto AVC_file = new AVC_File;
AVC_file->moveToThread(thread);
...
But AVC_FileCheck
is invoked from the main thread. Whether that's OK or not depends on how that method is implemented. It needs to be thread-safe, see this question for a discussion of that. TL;DR: The following pattern could be a starting point:
class AVC_file : public QObject {
Q_OBJECT
Q_SLOT void fileCheck_impl(QIODevice * dev) {
dev->setParent(this);
...
}
Q_SIGNAL void fileCheck_signal(QIODevice *);
public:
void fileCheck(QIODevice *dev) { fileCheck_signal(dev); }
AVC_file(QObject *parent = nullptr) : QObject(parent) {
connect(this, &AVC_file::fileCheck_signal, this, &AVC_file::fileCheck_impl);
...
}
};
Finally, your existing AVC_fileCheck
API is broken. You pass QFile
by reference: this won't ever work since it ceases to exist as soon as on_OpenAVCFile_clicked
returns. When AVC_file
uses that file in its thread, it's a dangling object reference.
Instead, you must pass the ownership of the file to AVC_file
, and pass a pointer to an instance that AVC_file
will dispose when done with. Or simply let AVC_file
open the file for you!
Qt: move to thread
It will live in the main thread, since you're calling worker->init()
from the main thread. You can either use signals and slots to call init
from the worker thread, or use QMetaObject::invokeMethod
with a queued connection (You don't have to specify this, as it will use Qt::AutoConnection
by default and that will use Qt::QueuedConnection
if invokeMethod
is called from a different thread than the receiving object).
QMetaObject::invokeMethod(worker, "init",
Qt::QueuedConnection);
You can also create myObject
in the constructor and set this
as the parent. Then when you call moveToThread
, the object will also move its children to the same thread.
QObject::moveToThread
: Changes the thread affinity for this object and its children.
Qt moveToThread: What resources are brought with the object?
As the Qt documentation for QObject::moveToThread states: -
Changes the thread affinity for this object and its children. The object cannot be moved if it has a parent. Event processing will continue in the targetThread.
In this case, a parent is an object whose child is set either by passing the parent in the constructor or by calling setParent on the child. It is not an an object which has a pointer to another object.
In the code, if I never set b's parent to a, and if I call movetothread() to move a into a worker thread, will b be moved into that thread too?
So, no, if b's parent is not set and you call moveToThread on 'a', 'b' will still have the original thread affinity.
If it is not moved, if I call b.init() from the worker thread...
If you've moved 'a' and not 'b' to a worker thread, then you should not be calling b.init directly from the worker thread. Instead, the object in the worker thread ('a') should emit a signal for an object in the original thread to call b.init from a connected slot
Create a QObject in another thread and retrieve it to the current thread = ASSERT failure in Debug on msvc16
So I debugged qt lib, the problem comes from setParent() doing a sendEvent()
without checking the thread affinity. The 2 objects lives in the same thread but the setParent() called is done from another one. Even though my operation isn't typical, it's still valid.. It's a simple bug or at least an unhandled case.
Depending on the calling thread it should do a postevent()
instead.
Finally I've just replaced obj->setParent(&mainObj)
by
QMetaObject::invokeMethod(&mainObj, [&mainObj, obj](){
obj->setParent(&mainObj);
});
This call is automatically done with a Queued connection and finally executed in the correct thread. Of course we have to start the eventloop in the main thread to retrieve this queued event.
This is an acceptable workaround
#include <QApplication>
#include <QDebug>
#include <QThread>
#include <QTimer>
class Thread : public QThread //Just a convenient Class using a lambda
{
public:
Thread::Thread(QObject *parent = nullptr) : QThread(parent){}
std::function<void()> todo;
protected:
virtual void run() override{
todo();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QObject mainObj;
Thread thread;
thread.todo = [&](){
//QObject *obj = new QObject(&mainObj); //"QObject: Cannot create children for a parent that is in a different thread." ! Of course!
// So we try this
QObject *obj = new QObject;
qDebug()<<obj->thread();
obj->moveToThread(mainObj.thread());
qDebug()<<obj->thread(); //Check that the Thread affinity change is done
QMetaObject::invokeMethod(&mainObj, [&mainObj, obj](){
obj->setParent(&mainObj);
});
QObject::connect(obj, &QObject::destroyed, [](){ //Parent mecanism is OK
qDebug()<<"Child destroyed";
});
};
thread.start();
thread.wait();
QTimer::singleShot(0,[](){
qApp->quit();
});
return a.exec(); //Add an event loop for the connect (queued) from the thread -> setParent() triggers a SendEvent() in the main thread where the two object now live
}
Related Topics
C++ Static Template Member, One Instance for Each Template Type
C++11 Thread Safety of Random Number Generators
Scoped Using-Directive Within a Struct/Class Declaration
Function Composition in C++/C++11
How to Programmatically Create a Shortcut Using Win32
Write Concurrently Vector<Bool>
Order of Execution in Operator <<
When to Use Shared_Ptr and When to Use Raw Pointers
Differencebetween a Template Class and a Class Template
C++11: Number of Variadic Template Function Parameters
What Is the Purpose of _Cxa_Pure_Virtual
How to Make a Heterogeneous Boost::Map
Why Does C++11 Have 'Make_Shared' But Not 'Make_Unique'
Random Number Generator That Produces a Power-Law Distribution