Qt-interest Archive, December 2006
Threads, signals etc.
Pages: Prev | 1 | 2 | Next
Message 1 in thread
Hallo,
I got problems w/signal delivery in threads. It seems I do not
understand completely what is going on. May be someone could
have a look over the attached example?
Basically it should implement a worker thread, which gets jobs thrown at
and executes these in order. The example job implemented here should
count down a number of times, everytime saying ping(), and after that
emit the completed() signal.
However, the ping() slot never gets called. I don't know if it's the
timer not activated for some reason, or if it's the thread's event loop
not delivering the signal.
Could someone look that over, please? (Any other comments on my approach
are welcome, too :-)
Thanks,
/eno
#include "worker.h"
#include "worker.h"
#include <QCoreApplication>
// extern "C"
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
worker w;
w.start();
w.sendJob(new countdown(5));
w.wait();
return 0;
}
#ifndef WORKER_H
#ifndef WORKER_H
#define WORKER_H
#include <QThread>
#include <QTimer>
#include <iostream>
class worker;
// A base class for a job.
class job: public QObject {
Q_OBJECT
friend class worker;
public:
//! execute the job
/*!
* This method starts the job.
* When done the completed() or failed() signal must
* be emitted.
*/
virtual void start() = 0;
signals:
//! This signal is emitted when the job starts.
void started();
//! This signal is emitted when the job is completed successfully.
void completed();
//! This signal is emitted when the job is completed w/failure.
void failed();
};
//! a worker thread
class worker: public QThread {
Q_OBJECT
QList<job*> jobs_;
job* activeJob_;
public:
worker()
{
activeJob_ = 0;
connect(this, SIGNAL(sendJobSignal(job*)), this, SLOT(onSendJob(job*)));
}
void sendJob(job* j)
{ emit sendJobSignal(j); }
protected:
void run()
{
//
// starts the event loop. The event loop runs until
// someone calls quit() or exit().
//
exec();
}
private:
void activateNextJob()
{
std::cout << "activateNextJob()" << std::endl;
assert(jobs_.size());
job* j = jobs_.front();
jobs_.pop_front();
//
// this emits the job's started() signal, to inform everyone that
// we are now really starting...
//
emit j->started();
connect(j, SIGNAL(completed()), this, SLOT(jobCompleted()));
connect(j, SIGNAL(failed()), this, SLOT(jobCompleted()));
try {
j->start();
}
catch(const std::exception& e) {
emit j->failed();
}
}
private slots:
void onSendJob(job* j)
{
jobs_.push_back(j);
if(activeJob_ == 0) activateNextJob();
}
void jobCompleted()
{
assert(dynamic_cast<job*>(sender()));
assert(sender() == activeJob_);
delete activeJob_;
activeJob_ = 0;
if(jobs_.size()) activateNextJob();
}
signals:
// emitted in response to sendJob(job*) and connected to onSendJob...
void sendJobSignal(job*);
};
class countdown: public job {
Q_OBJECT
int cnt_;
QTimer tm;
public:
countdown(int cnt = 3): cnt_(cnt)
{ }
void start()
{
connect(&tm, SIGNAL(timeout()), this, SLOT(ping()));
tm.start(100);
std::cout << "start()" << std::endl;
}
public slots:
void ping()
{
std::cout << "ping()" << std::endl;
if(--cnt_ < 0) emit completed();
}
};
#endif
TEMPLATE=app
TEMPLATE=app
win32:TEMPLATE=vcapp
SOURCES += worker.cpp
HEADERS += worker.h
QT += network
Message 2 in thread
On Monday 11 December 2006 18:25, Enrico Thierbach wrote:
> Could someone look that over, please? (Any other comments on my approach
> are welcome, too :-)
You should read http://doc.trolltech.com/4.2/threads.html. Specifically,
http://doc.trolltech.com/4.2/threads.html#accessing-qobject-subclasses-from-other-threads:
"Like other objects, QThread objects live in the thread where the object was
created -- not in the thread that is created when QThread::run() is called.
It is generally unsafe to provide slots in your QThread subclass, unless you
protect the member variables with a mutex."
and
http://doc.trolltech.com/4.2/threads.html#signals-and-slots-across-threads:
"With queued connections, the slot is invoked when control returns to the
event loop of the thread to which the object belongs. The slot is executed in
the thread where the receiver object lives.
With auto connections (the default), the behavior is the same as with direct
connections if the signal is emitted in the thread where the receiver lives;
otherwise, the behavior is that of a queued connection."
What you are seeing is expected behavior. Your QThread subclass belongs to the
GUI thread, so the GUI thread is supposed to call your slots. But since you
aren't running an event loop in the GUI thread, none of your slots get
called.
> Thanks,
> /eno
--
[ signature omitted ]
Message 3 in thread
Bradley T Hughes wrote:
> On Monday 11 December 2006 18:25, Enrico Thierbach wrote:
>> Could someone look that over, please? (Any other comments on my approach
>> are welcome, too :-)
Thank you, Bardley, for your answer.
The so-called "thread affinity" of Qt4's signal delivery is a wicked
thing, to say at least.
Now that I found QObject::moveToThread() - which I wasn't aware of - it
seems I can finally go on w/my workers/jobs thingy.
If I get it right it requires (signal/slot-)active objects that should
become active within a thread to be created from within the thread's
run() method, or to be moved to that thread via QObject::moveToThread.
Is that right?
And what is still unclear to me is on an queued connection which
object's thread's event loop is to be running: the sender's or the
recipient's.
More experimenting on my side... If anybody wants to see the results
stay tuned.
/eno
--
[ signature omitted ]
Message 4 in thread
On Tuesday 12 December 2006 11:44, Enrico Thierbach wrote:
> Bradley T Hughes wrote:
> > On Monday 11 December 2006 18:25, Enrico Thierbach wrote:
> >> Could someone look that over, please? (Any other comments on my approach
> >> are welcome, too :-)
>
> Thank you, Bardley, for your answer.
>
> The so-called "thread affinity" of Qt4's signal delivery is a wicked
> thing, to say at least.
> Now that I found QObject::moveToThread() - which I wasn't aware of - it
> seems I can finally go on w/my workers/jobs thingy.
>
> If I get it right it requires (signal/slot-)active objects that should
> become active within a thread to be created from within the thread's
> run() method, or to be moved to that thread via QObject::moveToThread.
> Is that right?
Yes, you create the object in the thread you want it to "live" in (or move it
there with QObject::moveToThread()).
> And what is still unclear to me is on an queued connection which
> object's thread's event loop is to be running: the sender's or the
> recipient's.
The receiver's - signals are immediate since they "signal" (pun intended) when
something happens. It's the slot that does the work, and as such, gets called
by the event loop running in the receiver's thread.
--
[ signature omitted ]
Message 5 in thread
Hi,
I've been working with QThread quite a lot recently.
The clearest way I found was :
- let the MyQThread object leave in the main GUI thread
- for each class MyQThread, systematically create a "utility class" called
MyQThreadInternal, which inherits from QObject.
- in MyQThread::run(), instanciate an object of type MyQThreadInternal and
start the thread event loop
The object MyQThreadInternal alwyas "leaves" in the thread, and the QThread
object still leaves in the GUI thread.
- now, all the work of the thread should be done within MyQThreadInternal
MyQThreadInternal may have various signals and slots, so as MyQThread.
Signal from MyQThreadInternal may be connected to signals of the same name
in MyQThread, or slots (and same the other way).
Hence, Qt will reemit asyncroneously from MyQThread, within the main GUI
thread, all signal emited within the thread itself, ie from
MyQThreadInternal.
I found this **architecture** the most easy to work with, especially because
all the objects' thread affinity is clear.
In case it helps...
Best-
Nicolas
Bradley T Hughes wrote:
> On Tuesday 12 December 2006 11:44, Enrico Thierbach wrote:
>> Bradley T Hughes wrote:
>> > On Monday 11 December 2006 18:25, Enrico Thierbach wrote:
>> >> Could someone look that over, please? (Any other comments on my
>> >> approach are welcome, too :-)
>>
>> Thank you, Bardley, for your answer.
>>
>> The so-called "thread affinity" of Qt4's signal delivery is a wicked
>> thing, to say at least.
>> Now that I found QObject::moveToThread() - which I wasn't aware of - it
>> seems I can finally go on w/my workers/jobs thingy.
>>
>> If I get it right it requires (signal/slot-)active objects that should
>> become active within a thread to be created from within the thread's
>> run() method, or to be moved to that thread via QObject::moveToThread.
>> Is that right?
>
> Yes, you create the object in the thread you want it to "live" in (or move
> it there with QObject::moveToThread()).
>
>> And what is still unclear to me is on an queued connection which
>> object's thread's event loop is to be running: the sender's or the
>> recipient's.
>
> The receiver's - signals are immediate since they "signal" (pun intended)
> when something happens. It's the slot that does the work, and as such,
> gets called by the event loop running in the receiver's thread.
>
>
--
[ signature omitted ]
Message 6 in thread
On Wednesday 13 December 2006 09:42, Nicolas Castagne wrote:
> The clearest way I found was :
> - let the MyQThread object leave in the main GUI thread
> - for each class MyQThread, systematically create a "utility class" called
> MyQThreadInternal, which inherits from QObject.
> - in MyQThread::run(), instanciate an object of type MyQThreadInternal and
> start the thread event loop
> The object MyQThreadInternal alwyas "leaves" in the thread, and the QThread
> object still leaves in the GUI thread.
>
> - now, all the work of the thread should be done within MyQThreadInternal
>
>
> MyQThreadInternal may have various signals and slots, so as MyQThread.
>
> Signal from MyQThreadInternal may be connected to signals of the same name
> in MyQThread, or slots (and same the other way).
> Hence, Qt will reemit asyncroneously from MyQThread, within the main GUI
> thread, all signal emited within the thread itself, ie from
> MyQThreadInternal.
>
> I found this **architecture** the most easy to work with, especially
> because all the objects' thread affinity is clear.
This is a very good idea, and it is actually the way I advocate using threads
in Qt. If everything goes my way, it will become the default way of doing
things.
<shameless-plug>
http://blogs.qtdeveloper.net/archives/2006/12/04/threading-without-the-headache/
</shameless-plug>
--
[ signature omitted ]
Message 7 in thread
Hi,
>> I found this **architecture** the most easy to work with, especially
>> because all the objects' thread affinity is clear.
>
> This is a very good idea, and it is actually the way I advocate using threads
> in Qt. If everything goes my way, it will become the default way of doing
> things.
>
> <shameless-plug>
> http://blogs.qtdeveloper.net/archives/2006/12/04/threading-without-the-headache/
> </shameless-plug>
>
While this is similar to what I am doing here meanwhile, one problem
remains: As, like in your example, the producer and consumer are created
not-from-the-thread they are supposed to live in, once they get active,
what happens with that objects, that are no childs of producer/consumer
respectively?
In short: can an object or a group of objects created outside a thread's
run() method moved to the thread and activated during the thread's run()
made behave as excepted, i.e. as they would behave in the context of the
thread they were created in?
The call to QObject::moveToThread() doesn't do anything to non-related
objects, their thread affinity remains unchanged. This, in turn,
requires all objects be managed on the heap, hence code like this:
class A: public QObject
{
QTimer tm;
A() { connect(&tm, SIGNAL(timeout()), ...); }
};
will never work as expected (And I love creating my QTimer's as static
class members ;-).
I see only two ways out of here
(0)
/* not applicable */ request all objects, that can be used like you
propose, to stick to a heap-managed, strictly related policy. I guess,
it is not applicable as you cannot just take current code, rewrap it a
bit and send it down the pipe. You cannot know for sure whether or not
there is an object embedded somewhere which doesn't get a parent or -
beware - the wrong one (qApp?)
Qt can certainly not warn in all that cases.
(1)
Make moveToThread virtual. In this case moveToThread had to ensure all
related objects are living in the right thread. Which would work,
somehow, at least.
(2)
Add automatic thread affinity. Instead of determining thread affinity
the moment an object is created, the thread affinity could be determined
the moment, the object is connected from/to for the very first time,
assuming that the context that happens is the "natural" habitat of the
object.
Just my two pennies here,
/eno
--
[ signature omitted ]
Message 8 in thread
I ma not sure to get everything in your last thread but...
> While this is similar to what I am doing here meanwhile, one problem
> remains: As, like in your example, the producer and consumer are created
> not-from-the-thread they are supposed to live in, once they get active,
> what happens with that objects, that are no childs of producer/consumer
> respectively?
What do you mean by an "active object" ?
And do you use the word "child" as in Qt (QObject::children) ?
Or as "child of a thread" (ie of the thread understood as a "process") ?
If it is the second... Well, I would not say that a QObject is the "child of
the thread", but that it is "affine with" the thread (or something like
this).
Now, basically, the "thread affinity" of a QObject when it is built is the
thread that is currently executing the constructor.
Indeed, I am quite sure that the thread affinity is choosen by Qt in
QObject::QObject(...) by calling simply
QThread::currentThread()
> In short: can an object or a group of objects created outside a thread's
> run() method moved to the thread and activated during the thread's run()
> made behave as excepted, i.e. as they would behave in the context of the
> thread they were created in?
Moved by calling QObject::moveToThread() ?
Then, I guess that all the connection (QObject::connect) to/from the
signal/slots of the object made BEFORE the move will keep their old
ConnectionType.
> The call to QObject::moveToThread() doesn't do anything to non-related
> objects, their thread affinity remains unchanged. This, in turn,
> requires all objects be managed on the heap, hence code like this:
>
> class A: public QObject
> {
> QTimer tm;
>
> A() { connect(&tm, SIGNAL(timeout()), ...); }
> };
>
> will never work as expected (And I love creating my QTimer's as static
> class members ;-).
Mmmhhh... Sure it will work as I would expect :=)
Since QTimer tm is NOT a class attribute (no "static" keyword), it is
constructed when the instance of A itself is constructed (ie : from the
constructor of A).
Hence, the thread affinity of tm will be the thread affinity of the instance
of A, ie, the thread affinity of BOTH will be the thread that actually
called the constructer.
No matter A is created on the heap or not.
Hence, for exemple, if in A::A() the timeout signal of tm is connected to a
slot in A, it will be a "direct connection".
And if a signal of another object, created by the main thread, before the
thread was started for example, is connected to a slot in A, it will be a
QueuedConnection :
the signal will be "posted" to the thread, and the slot will be executed by
the thread's event loop.
You may change this later by calling QObject::moveToThread(). But, of course
changing the thread affinity of the instance of A will not change the
thread affinity of the tm.
Now, I think you may not create QTimer tm as a class object (ie with the
static keyword), because, I think, Qt does not allow you creating QObject
before QApplication is constructed.
Hope it helps !
Nicolas
Enrico Thierbach wrote:
> Hi,
>
>>> I found this **architecture** the most easy to work with, especially
>>> because all the objects' thread affinity is clear.
>>
>> This is a very good idea, and it is actually the way I advocate using
>> threads in Qt. If everything goes my way, it will become the default way
>> of doing things.
>>
>> <shameless-plug>
>>
http://blogs.qtdeveloper.net/archives/2006/12/04/threading-without-the-headache/
>> </shameless-plug>
>>
>
>
> While this is similar to what I am doing here meanwhile, one problem
> remains: As, like in your example, the producer and consumer are created
> not-from-the-thread they are supposed to live in, once they get active,
> what happens with that objects, that are no childs of producer/consumer
> respectively?
>
> In short: can an object or a group of objects created outside a thread's
> run() method moved to the thread and activated during the thread's run()
> made behave as excepted, i.e. as they would behave in the context of the
> thread they were created in?
>
> The call to QObject::moveToThread() doesn't do anything to non-related
> objects, their thread affinity remains unchanged. This, in turn,
> requires all objects be managed on the heap, hence code like this:
>
> class A: public QObject
> {
> QTimer tm;
>
> A() { connect(&tm, SIGNAL(timeout()), ...); }
> };
>
> will never work as expected (And I love creating my QTimer's as static
> class members ;-).
>
> I see only two ways out of here
>
> (0)
>
> /* not applicable */ request all objects, that can be used like you
> propose, to stick to a heap-managed, strictly related policy. I guess,
> it is not applicable as you cannot just take current code, rewrap it a
> bit and send it down the pipe. You cannot know for sure whether or not
> there is an object embedded somewhere which doesn't get a parent or -
> beware - the wrong one (qApp?)
>
> Qt can certainly not warn in all that cases.
>
> (1)
>
> Make moveToThread virtual. In this case moveToThread had to ensure all
> related objects are living in the right thread. Which would work,
> somehow, at least.
>
> (2)
>
> Add automatic thread affinity. Instead of determining thread affinity
> the moment an object is created, the thread affinity could be determined
> the moment, the object is connected from/to for the very first time,
> assuming that the context that happens is the "natural" habitat of the
> object.
>
> Just my two pennies here,
> /eno
>
> --
> To unsubscribe - send a mail to qt-interest-request@xxxxxxxxxxxxx with
> "unsubscribe" in the subject or the body. List archive and information:
> http://lists.trolltech.com/qt-interest/
--
[ signature omitted ]
Message 9 in thread
Hi,
> Now, I think you may not create QTimer tm as a class object (ie with the
> static keyword), because, I think, Qt does not allow you creating QObject
> before QApplication is constructed.
Generally speaking global static objects are not a good idea because the
order of instantiation depends on the compiler (some older compilers
wouldn't even instantiate a static object in some cases).
In a threaded environment, any static variable may cause problems when
initialized/accessed from two different threads.
--
[ signature omitted ]
Message 10 in thread
On Wednesday 13 December 2006 13:20, Enrico Thierbach wrote:
> Hi,
>
> >> I found this **architecture** the most easy to work with, especially
> >> because all the objects' thread affinity is clear.
> >
> > This is a very good idea, and it is actually the way I advocate using
> > threads in Qt. If everything goes my way, it will become the default way
> > of doing things.
> >
> > <shameless-plug>
> > http://blogs.qtdeveloper.net/archives/2006/12/04/threading-without-the-he
> >adache/ </shameless-plug>
>
> While this is similar to what I am doing here meanwhile, one problem
> remains: As, like in your example, the producer and consumer are created
> not-from-the-thread they are supposed to live in, once they get active,
> what happens with that objects, that are no childs of producer/consumer
> respectively?
Then you have to move them yourself (see below).
> In short: can an object or a group of objects created outside a thread's
> run() method moved to the thread and activated during the thread's run()
> made behave as excepted, i.e. as they would behave in the context of the
> thread they were created in?
Yes, that's the whole point of QObject::moveToThread() :)
> The call to QObject::moveToThread() doesn't do anything to non-related
> objects, their thread affinity remains unchanged. This, in turn,
> requires all objects be managed on the heap, hence code like this:
>
> class A: public QObject
> {
> QTimer tm;
>
> A() { connect(&tm, SIGNAL(timeout()), ...); }
> };
>
> will never work as expected (And I love creating my QTimer's as static
> class members ;-).
As *static* members, or normal members?
> I see only two ways out of here
>
> (0)
>
> /* not applicable */ request all objects, that can be used like you
> propose, to stick to a heap-managed, strictly related policy. I guess,
> it is not applicable as you cannot just take current code, rewrap it a
> bit and send it down the pipe. You cannot know for sure whether or not
> there is an object embedded somewhere which doesn't get a parent or -
> beware - the wrong one (qApp?)
Perhaps I missing something, but if you wrote the code, you should know what
parent your objects do or don't have :)
> Qt can certainly not warn in all that cases.
>
> (1)
>
> Make moveToThread virtual. In this case moveToThread had to ensure all
> related objects are living in the right thread. Which would work,
> somehow, at least.
You can reimplement QObject::event() and watch for the QEvent::ThreadChange
event and do whatever you want (like move your static timers...).
> (2)
>
> Add automatic thread affinity. Instead of determining thread affinity
> the moment an object is created, the thread affinity could be determined
> the moment, the object is connected from/to for the very first time,
> assuming that the context that happens is the "natural" habitat of the
> object.
This sounds like a good idea, but doesn't work in practice. People normally do
things in constructors that would cause the thread affinity to be set (like
signal/slot connects, starting timers, etc.)
--
[ signature omitted ]
Message 11 in thread
Hi Bradley,
>> In short: can an object or a group of objects created outside a thread's
>> run() method moved to the thread and activated during the thread's run()
>> made behave as excepted, i.e. as they would behave in the context of the
>> thread they were created in?
>
> Yes, that's the whole point of QObject::moveToThread() :)
I know. But see below, please.
>> class A: public QObject
>> {
>> QTimer tm;
>>
>> A() { connect(&tm, SIGNAL(timeout()), ...); }
>> };
>>
>> will never work as expected (And I love creating my QTimer's as static
>> class members ;-).
>
> As *static* members, or normal members?
As a normal member (See the code :-). I know this is easily worked
around by
class A: public QObject
{
QTimer* tm;
A() { tm = new QTimer(this); connect(&tm, SIGNAL(timeout()), ...); }
};
but besides that it costs a bit (an additional allocation on the heap) I
feel it strange that the latter works while the former cannot.
Note: I am used to use the QObject child/parent-relationship for exactly
two purposes:
1) Defining a hierarchy of objects
2) Defining ownership of objects
While even this is not-the-best-design-ever (what if the owner of an
object is not its hierarchical parent, but, say, a list of objects or -
horror - a shared pointer object of some kind) this gets simply overly
used by adding additional meaning to it, here how thread affinity is
determined and how moveToThread() works.
>>
>
>> I see only two ways out of here
>>
>> (0)
>>
>> /* not applicable */ request all objects, that can be used like you
>> propose, to stick to a heap-managed, strictly related policy. I guess,
>> it is not applicable as you cannot just take current code, rewrap it a
>> bit and send it down the pipe. You cannot know for sure whether or not
>> there is an object embedded somewhere which doesn't get a parent or -
>> beware - the wrong one (qApp?)
>
> Perhaps I missing something, but if you wrote the code, you should know what
> parent your objects do or don't have :)
>
>> Qt can certainly not warn in all that cases.
>>
>> (1)
>>
>> Make moveToThread virtual. In this case moveToThread had to ensure all
>> related objects are living in the right thread. Which would work,
>> somehow, at least.
>
> You can reimplement QObject::event() and watch for the QEvent::ThreadChange
> event and do whatever you want (like move your static timers...).
>
Does this "we use events instead of virtual methods" thingy Qt is
currently driving to comes from the quest for binary compatibility
between 4.x releases? The cost for this is high: extending the API in a
binary compatible manner can be done only without new virtual methods.
This leads to adding new interfaces through the backdoor, like using new
event types, new enums etc. Even if they were well-documented - which
theay aren't in all cases - this lead to a state where a programmer just
"needs to know" instead of simply looking at what is exposed in the C++
interfaces.
If you come from a solid C++ background you should get along easily with
the interface description of a class. (i.e. signals, slots, methods,
virtual methods and the like.) These days Qt tends to hide a lot of an
object's interface in enums and co. While this is a coherent move (type
safety was obviously not a top-10 priority when QObject::connect was
designed and re-designed for qt4) it doesn't help the API users to come
to quick result: you must *simply know* really much about Qt4's
internals to do some stuff.
In this case a natural choice for a C++ classicist would be to make
moveToThread() virtual to let derived objects do whatever they need to
do. This is something that can clearly be seen in the classes interface.
Brad, as I know you are Qt's multithreading man I hope to get through to
you with this, and would really like to have certain aspects of the Qt4
interface changed in Qt5.
/eno
Side-Notes:
MoveToThread seems poorly named. Actually, the object gets moved to a
different "event loop". And while a thread "can have its own event loop"
as the docs state, you cannot do much with that event loop - in
constrast to the main event loop in Q(Core)Application. This seems
strange to me. Couldn't QThread expose its internal event loop via some
method?
--
[ signature omitted ]
Message 12 in thread
> Side-Notes:
>
> MoveToThread seems poorly named. Actually, the object gets moved to a
> different "event loop".
Ok: please drop the "poorly named" line. Is my recently ;-) aquired
understanding correct that "moveToThread" would move an object to a
whole bunch of event loops: namely these that run within a spcified thread?
/eno
--
[ signature omitted ]
Message 13 in thread
On Thursday 14 December 2006 18:52, Enrico Thierbach wrote:
[snip]
> >> A() { connect(&tm, SIGNAL(timeout()), ...); }
> >>
> >> will never work as expected (And I love creating my QTimer's as static
> >> class members ;-).
> >
> > As *static* members, or normal members?
>
> As a normal member (See the code :-). I know this is easily worked
> around by
[snip]
> A() { tm = new QTimer(this); connect(&tm, SIGNAL(timeout()), ...); }
>
> but besides that it costs a bit (an additional allocation on the heap) I
> feel it strange that the latter works while the former cannot.
In one case, you specify the parent, while in the second you don't. Doesn't
seem strange to me at all.
> Note: I am used to use the QObject child/parent-relationship for exactly
> two purposes:
>
> 1) Defining a hierarchy of objects
> 2) Defining ownership of objects
>
> While even this is not-the-best-design-ever (what if the owner of an
> object is not its hierarchical parent, but, say, a list of objects or -
> horror - a shared pointer object of some kind) this gets simply overly
> used by adding additional meaning to it, here how thread affinity is
> determined and how moveToThread() works.
I'm having trouble parsing this :) Could you reword it, perhaps?
[snip]
> Brad, as I know you are Qt's multithreading man I hope to get through to
> you with this, and would really like to have certain aspects of the Qt4
> interface changed in Qt5.
There's nothing I can do about that. Keeping the whole Qt 5 series ABI
compatible will require alot of the same techniques you see in Qt 4 (which
were there in Qt 2 & 3 as well).
> Side-Notes:
>
> MoveToThread seems poorly named. Actually, the object gets moved to a
> different "event loop". And while a thread "can have its own event loop"
> as the docs state, you cannot do much with that event loop - in
> constrast to the main event loop in Q(Core)Application. This seems strange
> to me.
I disagree. The method seem very aptly named, and the documentation backs it
up. QObject::moveToThread() changes what QObject::thread() returns; it
changes the object's thread affinity. And thread affinity is defined as the
thread responsible for delivering events to the object.
Giving the method a name that describes what it does to the internals
> Couldn't QThread expose its internal event loop via some method?
--
[ signature omitted ]
Message 14 in thread
Bradley T Hughes wrote:
> On Thursday 14 December 2006 18:52, Enrico Thierbach wrote:
> [snip]
>>>> A() { connect(&tm, SIGNAL(timeout()), ...); }
>>>>
>>>> will never work as expected (And I love creating my QTimer's as static
>>>> class members ;-).
>>> As *static* members, or normal members?
>> As a normal member (See the code :-). I know this is easily worked
>> around by
> [snip]
>> A() { tm = new QTimer(this); connect(&tm, SIGNAL(timeout()), ...); }
>>
>> but besides that it costs a bit (an additional allocation on the heap) I
>> feel it strange that the latter works while the former cannot.
>
> In one case, you specify the parent, while in the second you don't. Doesn't
> seem strange to me at all.
This is ok, as long as you have control over the source. (and finally
you hinted me to QEvent::ThreadChange) If you don't have it you might
find yourself in a position where you have an object which works nicely
on the main loop, but once you move it to a different thread it
currently stops working.
>
>> Note: I am used to use the QObject child/parent-relationship for exactly
>> two purposes:
>>
>> 1) Defining a hierarchy of objects
>> 2) Defining ownership of objects
>>
>> While even this is not-the-best-design-ever (what if the owner of an
>> object is not its hierarchical parent, but, say, a list of objects or -
>> horror - a shared pointer object of some kind) this gets simply overly
>> used by adding additional meaning to it, here how thread affinity is
>> determined and how moveToThread() works.
>
> I'm having trouble parsing this :) Could you reword it, perhaps?
Here it goes ;-)
Hierarchy of objects: defines an abstraction where one object is related
to zero or more objects in a way that you can name them parents or
childs. Read: tree,nodes,leaves etc.pp.
Ownership of objects: an object o1 is owned by another object o2 <==> if
o2 gets destroyed o1 gets destroyed as well.
While these two concepts do often come in pairs they are not identical.
For example an object could be part of two different hierarchies:
obviously in such a case ownership cannot be taken by one or the other
hierarchy, but must handled externally.
Adding the "thread affinity issue" just adds a third notion to the
already heavily loaded QObject::children infrastructure: this time the
assumption that a child of an object has the same thread affinity, and
even more: a non-child is affine to the thread it creates it.
Note: the assumption that a child lives in the same thread as its parent
is certainly useful in most cases. However, additional steps might have
to be taken to fix thread affinity in some cases, and an API which
allows that (and is accessible via the docs) should be added to do that.
>
> [snip]
>> Brad, as I know you are Qt's multithreading man I hope to get through to
>> you with this, and would really like to have certain aspects of the Qt4
>> interface changed in Qt5.
>
> There's nothing I can do about that. Keeping the whole Qt 5 series ABI
> compatible will require alot of the same techniques you see in Qt 4 (which
> were there in Qt 2 & 3 as well).
>
I hope with "Keeping the whole Qt 5..." you don't mean Qt5 to be
ABI-compatible with Qt4? And hopefully something can be done towards Qt
5. Another argument supporting my stance may be on the time of invocation:
the API user does know when the "moveToThread()" method gets called (in
exact the moment it gets called :) If the object had to handle the event
it cannot know th exact time of invocation, but only guess.
Besides in one of your previous mails you said:
>>> You can reimplement QObject::event() and watch for the
>>> QEvent::ThreadChange event and do whatever you want
>>> (like move your static timers...).
This is actually a very precious hint. Even more as the docs (at least
for Qt/Mac 4.2.1) dont say a word on this ;-)
>> Side-Notes:
>>
>> MoveToThread seems poorly named. Actually, the object gets moved to a
>> different "event loop". And while a thread "can have its own event loop"
>> as the docs state, you cannot do much with that event loop - in
>> constrast to the main event loop in Q(Core)Application. This seems strange
>> to me.
>
> I disagree. The method seem very aptly named, and the documentation backs it
> up. QObject::moveToThread() changes what QObject::thread() returns; it
> changes the object's thread affinity. And thread affinity is defined as the
> thread responsible for delivering events to the object.
But is it really the thread who delivers the events? The way I see it an
event is sent directly via QObject::event() and friends, which is
obviously only save if QObject::thread() is the current thread, or must
be posted via QCoreApplication::postEvent() which then must add the
event to the target's thread's event loop.
Right?
However, meanwhile I realized that *multiple* event loops (or at least
QEventLoop objects) can live in a thread. Hence a name suggesting the
object be moved to some event loop wouldn't be correct either.
>
> Giving the method a name that describes what it does to the internals
>
>> Couldn't QThread expose its internal event loop via some method?
>
I missed the answer to that?
Have a nice weekend,
/eno
--
[ signature omitted ]
Message 15 in thread
On Friday 15 December 2006 21:15, Enrico Thierbach wrote:
> While these two concepts do often come in pairs they are not identical.
> For example an object could be part of two different hierarchies:
> obviously in such a case ownership cannot be taken by one or the other
> hierarchy, but must handled externally.
From a theoretical point of view, yes, having a single object in multiple
hierarchies is possible, but not in Qt :)
> Adding the "thread affinity issue" just adds a third notion to the
> already heavily loaded QObject::children infrastructure: this time the
> assumption that a child of an object has the same thread affinity, and
> even more: a non-child is affine to the thread it creates it.
>
> Note: the assumption that a child lives in the same thread as its parent
> is certainly useful in most cases. However, additional steps might have
> to be taken to fix thread affinity in some cases, and an API which
> allows that (and is accessible via the docs) should be added to do that.
I don't see the problem. If you don't like the default affinity, change it.
This is what QObject::moveToThread() is for...
> I hope with "Keeping the whole Qt 5..." you don't mean Qt5 to be
> ABI-compatible with Qt4? And hopefully something can be done towards Qt
> 5. Another argument supporting my stance may be on the time of invocation:
I'm not talking about keeping Qt 5 ABI compatible with Qt 4; I'm talking about
Qt 5.x being compatible with 5.y.
> the API user does know when the "moveToThread()" method gets called (in
> exact the moment it gets called :) If the object had to handle the event
> it cannot know th exact time of invocation, but only guess.
The event is sent at the end of moveToThread(), so you know that if you get a
ThreadChange event, moveToThread() has just finished what it needs to do and
is about to return.
> Besides in one of your previous mails you said:
> >>> You can reimplement QObject::event() and watch for the
> >>> QEvent::ThreadChange event and do whatever you want
> >>> (like move your static timers...).
>
> This is actually a very precious hint. Even more as the docs (at least
> for Qt/Mac 4.2.1) dont say a word on this ;-)
Indeed, I'll update the docs.
> But is it really the thread who delivers the events? The way I see it an
> event is sent directly via QObject::event() and friends, which is
> obviously only save if QObject::thread() is the current thread, or must
> be posted via QCoreApplication::postEvent() which then must add the
> event to the target's thread's event loop.
>
> Right?
Yes, it is the thread that delivers events to QObject::event().
> However, meanwhile I realized that *multiple* event loops (or at least
> QEventLoop objects) can live in a thread. Hence a name suggesting the
> object be moved to some event loop wouldn't be correct either.
Right. That's why the method is called moveToThread().
> >> Couldn't QThread expose its internal event loop via some method?
>
> I missed the answer to that?
Our standard answer: private impementation (P-IMPL). We don't expose the
internals of Qt :)
--
[ signature omitted ]
Pages: Prev | 1 | 2 | Next