I understand how QThread
is supposed to be used conceptually, with something akin to the following
MyWorker *worker = new MyWorker();
QThread *thread = new QThread();
worker->moveToThread(thread);
connect(thread, SIGNAL(started()), worker, SLOT(doWork()));
connect(worker, SIGNAL(done()), thread, SLOT(quit()));
connect(worker, SIGNAL(done()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
which will execute the doWork()
method within MyWorker
within the thread
, stopping the thread and performing memory cleanup once worker
announces it's done via done()
. This is how I've seen all examples, and it really appears geared towards a Worker does an operation in a separate thread type of use case.
Based on what I've understood:
Qt::AutoConnection
will use Qt::DirectConnection
if the signal and slot objects live in the same threadQt::AutoConnection
will use Qt::QueuedConnection
if the signal and slot objects live in a different threadQt::DirectConnection
will execute the destination slot in the same thread as the source signalQt::QueuedConnection
will queue the signal such that it will (eventually) be executed within the event loop of destination slot's threadmoveToThread()
"relocates" an object from its current thread into the specified oneQThread
will remain active (regardless of the objects within) until QThread::quit()
is called (which is why the connect
to SLOT(quit())
in the above code example is so importantSo this leads me to two questions for a potential use case which feels like it goes a little against the prescribe QThread
formula, and which I haven't really been able to find discussed anywhere.
By this I mean:
MyWorker *worker = new MyWorker();
QThread *workerThread = new QThread();
worker->moveToThread(workerThread);
connect(workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(workerThread, SIGNAL(finished(), thread, SLOT(deleteLater()));
workerThread->start();
In the above, there is no dedicated "execute" slot/method within MyWorker
, meaning that it would perform any triggers slots in thread
(provided the connection was done using Qt::AutoConnection
or Qt::QueuedConnection
of course). Thus if elsewhere in the code I were to do:
MyTrigger trigger;
connect(&trigger, SIGNAL(triggered()), worker, SLOT(doSomething()));
then any time trigger
would emit the triggered()
signal, worker
would execute the doSomething()
slot within workerThread
rather than whatever thread is trigger
belongs to? Thus, slot processing would be multi-threaded without there being an explicit doWork()
item to be executed in the thread.
Note: I can see the issues with memory management in the example above, but this is just purely an illustration of the idea.
I'm fairly confident that this would be the case, and if so this leads me to my next question...
Or perhaps a slightly different way to phrase this would be, can I ensure that objects are created in a particular thread? Continuing from the above example, let's say that I want to replace MyWorker
with some kind of wider capability, where I want the whole capability to operate in this separate thread. I'm thinking of essentially expanding on the above idea, but rather than using a single class, leveraging a series of classes, all of which are built-out from a central class. What I'm thinking is something along the lines of
class MyCapability: public QObject {
Q_OBJECT
public:
MyOtherWorker* getOtherWorker() {
return worker2;
}
public slots:
void setup() {
worker1 = new MyWorker();
worker2 = new MyOtherWorker();
connect(worker1, SIGNAL(something()), worker2, SLOT(other()));
}
void doSomething() {
worker1->doSomething();
}
private:
MyWorker *worker1;
MyOtherWorker *worker2;
};
MyCapability *capability = new MyCapability();
QThread *capabilityThread = new QThread();
capability->moveToThread(capabilityThread);
connect(capabilityThread, SIGNAL(started()), capability, SLOT(setup()));
connect(capabilityThread, SIGNAL(finished()), capability, SLOT(deleteLater()));
connect(capabilityThread, SIGNAL(finished(), thread, SLOT(deleteLater()));
capabilityThread->start();
Thus:
capability
will live in the capabilityThread
MyCapability::setup()
is called within capabilityThread
anything created within will belong to capabilityThread
as wellworker1::something()
connects to worker2::other()
as Qt::DirectConnection
as they are within the same threadWhen this is used from external
MyTrigger trigger;
connect(&trigger, SIGNAL(doSomethingTrigger()), capability, SLOT(doSomething()));
connect(&trigger, SIGNAL(doOtherTrigger()), capability->getOtherWorker(), SLOT(other()));
trigger
will be connecting via Qt::QueueConnection
as capability
, worker1
, and worker2
all live within the capabilityThread
(assuming trigger
is in a different one).
What I'm thinking is creating a "pocket" of functionality in this manner that exists in a single thread, separate from the rest of the application, such that the rest of the application can largely ignore what thread it may (or may not) live in, but which will perform its processing/execution in its dedicated thread via signals/slots in a safe and reliable manner.
Note: again, this example code is a bit of a minefield in terms of memory stability/management, but I'm trying to make sure that my understanding of the QThread
concepts is correct.
Is my understanding on these two fronts correct? What mistaken assumptions am I working from?
Thanks!
Thus, slot processing would be multi-threaded without there being an explicit doWork() item to be executed in the thread
Yes, whole point of workers is to not have explicit while (haveWork()) doWork()
(it's kinda still there though), just an eventloop that ready to execute tasks as soon as they arrive.
What thread is an object created in?
Function creates an object in a thread it runs in (current thread). setup()
will be executed in capabilityThread
, so worker1
and worker2
will be created and live in capabilityThread
.
Notice that if you want to access data from another thread not by signal-slot mechanism, you need to use syncronisation (QMutex
) (like in capability->getOtherWorker()
).
You can validate all your assumptions about what runs where using qDebug() << QThread::currentThreadId();
such that the rest of the application can largely ignore what thread it may (or may not) live in
It will only work if you do every data access using signal-slot mechanism (request data, send data). Otherwise you need syncronisation.
Take a look at Qt Concurrent. It has some nice high level multithreading primitives (QFuture
, QPromise
, map-reduce, and thread pool).