[Qt-interest] Question about the thread affinity of a QThread object
Sean Harmer
sean.harmer at maps-technology.com
Fri May 21 10:53:37 CEST 2010
Hi,
On Thursday 20 May 2010 23:29:36 K. Frank wrote:
> As I understand it, if the run() method of QThread t1 executes
>
> QThread *t2 = new QThread;
>
> the thread affinity of t2 will be for t1 (rather than itself, t2).
This is correct.
> Now, I _think_ I have seen warnings, maybe in the documentation, or maybe
> in a list discussion, that one should not emit signals from a QThread's
> run() method.
Emitting signals from a QThread object (even in its run() function) is fine. I
think the warnings you are referring to were about not putting *slots* in the
QThread object and instead having worker objects created in run() to do the
work.
It's not that you can't do this, just that it is clearer in which thread
context the slots will get executed. Remember that the QThread object itself
has affinity with its parent object which lives in the thread which created
the QThread object in question - unless you explicitly move it of course.
To avoid confusion when it comes to maintenance time 2 years down the line and
the new employee fails to spot the importance of your moveToThread() call and
removes it then bad things will happen. If you explicitly create worker
objects in QThread::run() it becomes much clearer to see that the slots of
these objects will be executed in the context of your QThread object and not
the thread your QThread object has affinity to.
> The reasoning (if I remember correctly) was that mismatch
> between the QThread's thread affinity and the thread of execution that
> executes run(). (The general design advice was to have the QThread create
> additional objects that would naturally have the "correct" thread affinity
> to do the work.)
It is the thread affinity of the receiving object that determines in which
thread of execution the slot is executed. As Thiago said emitting signals is
thread safe. If the signal is to be delivered to an object in another thread
it is converted to an event and posted to the receiving object's affine thread
for delivery.
>
> class Emitter : public QThread {
> public:
> void run() {
> emit send();
> }
> public signals:
> void send();
> };
>
> class Receiver : public QThread {
> public:
> void run() {
> QThread *t2 = new Emitter;
> connect (t2, SIGNAL(send()), this, SLOT(receive());
> t2->start();
> exec();
> }
> public slots:
> void receive();
> };
>
> Suppose a thread, t0, (perhaps the main gui thread) executes
>
> QThread *t1 = new Receiver; // t1's thread affinity starts out for t0
> t1->moveToThread (t1); // but now t1's thread affinity is for itself
> t1->start(); // t1's run() method creates and starts t2
>
> Does this break anything?
No, it just means that the receive() slot will be called in the context of the
Receiver thread as opposed to the main thread (where t1 was created). I guess
this is what you want in this case.
Note how it would be much clearer if your thread was perhaps called
ReceiverThread and simply created a Receiver object derived from QObject with
a receive() slot. That way there would be no confusion.
When creating threads that are to be event driven - ie they will have a
running event loop - then I tend to think of the QThread::run() function as
being the equivalent of main() in the main thread. That is I simply create one
or more objects; plumb them together with some connects if needed; and call
exec() to start the event loop.
> As I understand it, t1's thread affinity is for itself, t1 (because we
> moved it), and
> t2's thread affinity is for t1 (not for itself, t2).
Correct.
> t2's run() method emits the send() signal, which is connected to t1's
> receive() slot. Which thread of execution actually calls t1->receive()?
t1's, because that is the thread with which the receiver object (t1 itself in
this case) has affinity with. Also that is where the event loop is running. If
you had not started the event loop in t1 then the slot would not be called
until you did start it.
> My recollection about the warnings against having t2 emit a signal was that
> t2's "confused" thread affinity (for t1 rather than itself) could cause
> the signal to call
> the slot incorrectly through a direct connection.
The connection type to use (queued/direct etc) is determined when you call
emit (unless you force it to something else in your connect call). The signal
is being emitted from within t2's context and is connected to t1 which has
affinity with t1 and so a queued connection is used.
> But according to Thiago's comment, t2's thread affinity has nothing to do
> with how the slot gets called.
>
> t2's thread of execution calls t2->run(), which calls emit send();.
> As I understand
> Thiago's comment, the thread of execution, t2, (not t2's affinity, which is
> t1) determines how the signal / slot connection is resolved. Because the
> emitter's thread of execution, t2, and the receivers thread affinity, t1,
> are different, the
> signal is propagated to the slot through a queued connection, and therefore
> t1->receiver() is called by t1's thread of execution.
>
> First, is this analysis correct?
I think so.
> Second, if it is, what's the problem with having t2 emit a signal?
There is no problem.
> If t2's thread affinity is irrelevant to how the signal is propagated, and
> therefore the signal / slot connection is (properly) a queued connection,
> what are all the warnings about?
I think you imagined them wrt emitting signals from a thread. I do not think
emitting signals form a thread is a problem. The Mandelbrot example does this
so even the trolls do it :-)
ATB,
Sean
More information about the Qt-interest-old
mailing list