[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