[Qt-interest] Threading and objects...
BRM
bm_witness at yahoo.com
Thu Nov 11 17:54:36 CET 2010
I have been doing quite a bit of multithreading with Qt for a while; and
recently read (on the Qt blogs, a couple months back[1]) about the "right way"
to use QThread; I finally had an opportunity to try it out but am running into a
little problem - at least, I'm getting a message logged. I mainly use threads to
do various tasks - typically ending up with a specific object (e.g. a QTcpSocket
derived class) to a thread; usually something I/O bound or processing intensive
so the one-QObject-to-one-QThread is desired.
Traditionally, I have derived a new class from QThread, aka QMyThread. Within
the new class's run() I then allocate a new QObject derived class
(QMyThreadInstance) that does the core work; sometimes passing another class
(e.g. a QTcpSocket derived class) to this new class, which then does parent the
class. The instance class (QMyThreadInstance) does not have a parent, and gets
cleaned up when the event loop exits. The thread class (QMyThread) has a series
of signals for an outside object (e.g. the object spawning the thread) to
connect to, which it then passes on to the instance class (QMyThreadInstance).
As a result, it does take some time to setup these classes, and such. Further, I
don't parent the instance of the QThread (QMyThread) when I allocate it.
(Example 1 below, [2])
In my new approach, I'm trying to keep the same basic architecture, at least for
now while I get to figure out the new approach - mostly so that it is easy to
revert if I needed to, while at the same time trying to do it the "right way"
with the goal to make it all simpler in the end, perhaps migrating the rest of
the entries to the new method as well. In following this approach, I created a
manager class (QMyThreadManager) that allocates and runs a standard instance of
a QThread - just allocates and starts it. I did parent the QThread to the class,
namely so it gets cleaned up when the manager class goes away. I also create an
instance class (QMyThreadInstance) in the same manner, it is not parented; and I
use moveToThread() on it to move it to the QThread instance that was just
created. When I have another object to give it - e.g. a QTcpSocket derived class
- I first call setParent(NULL) on it, then I do a moveToThread as well, to move
it to the thread. This is all done before the thread is started, all of which
(including staring the thread) is in or run (directly or indirectly) the class
constructor. (Example 2 below, [3] & [4]).
The problem I am running into is that I am getting the following message on the
new approach:
QObject::setParent: Cannot set parent, new parent is in a different thread
Now, I'm probably doing something really stupid and just not seeing it. As the
descriptions above are probably not quite adequate, I put two examples below of
each method.
TIA,
Ben
[1] I think it was this one -
http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/ - and the one it links
to but cannot be certain any more. It's been quite a while.
[2] Example 1: My traditional method
// QMyThrad is instantiated by another class which does not parent it - but
connects the signals/slots so it auto-stops/destroys.
// existing_connection is allocated in the main thread, typically by a
QTcpServer derived class and retrieved via nextPendingConnection()
// additionally, it had setParent(NULL) and
moveToThread(pointerToInstanceOfQMyThread) called on it
// only run() is important - it's just the constructor/destructor otherwise.
void QMyThread::run()
{
if (threadRunner == NULL) {
if (existing_connection == NULL) {
threadRunner = new QMyThreadInstance(NULL); // standard constructor
} else {
threadRunner = new QMyThreadInstance(existing_connection,NULL); //
overloaded constructor
}
}
if (threadRunner != NULL) {
existing_connection = NULL; // it's been handed to the object if we had
it.
// connect object's signals to our signals
// connect our signals to object's slots
exec();
if (threadRunner == NULL) {
delete threadRunner;
threadRunner = NULL;
}
}
}
[3] Example 2: new method
// QMyThreadManager is instantiated by another class which does parent it - e.g.
new QMyThreadManager(this)
// existing_connection is allocated in the main thread, typically by a
QTcpServer derived class and retrieved via nextPendingConnection()
QMyThreadManager::QMyThreadManager(QObject* _parent) : QObject(_parent) {
existing_connection = NULL;
createThread();
}
QMyThreadManager::QMyThreadManager(QTcpSocket* _existing_connection, QObject*
_parent) : QObject(_parent) {
existing_connection = _existing_connection;
createThread();
}
void QMyThreadManager::createThread() {
if (actualThread.isNull() == true) {
actualThread = new QThread(this);
}
if (actualThread.isNull() == false) {
if (threadRunner.isNull() == true) {
if (existing_connection == NULL) {
threadRunner = new QMyThreadInstance(NULL);
} else {
// this use to be done by the allocating class
existing_connection.setParent(NULL); // ensure no parent is set!
existing_connection.moveToThread(actualThread); // move it to
the new thread
threadRunner = new QMyThreadInstance(existing_connection,NULL);
}
}
if (threadRunner.isNull() == false) {
existing_connection = NULL; // it's been handed to the object if we
had it
// connect object's signals to our signals
// connect our signals to object's slots
threadRunner->moveToThread(actualThread); // move it to the new
thread
actualThread->start(); // run the new thread
} else {
delete actualThread;
}
}
}
[4] Example 2: new method (alternate) - eliminates parenting the QThread
// QMyThreadManager is instantiated by another class which does parent it - e.g.
new QMyThreadManager(this)
// existing_connection is allocated in the main thread, typically by a
QTcpServer derived class and retrieved via nextPendingConnection()
QMyThreadManager::QMyThreadManager(QObject* _parent) : QObject(_parent) {
existing_connection = NULL;
createThread();
}
QMyThreadManager::QMyThreadManager(QTcpSocket* _existing_connection, QObject*
_parent) : QObject(_parent) {
existing_connection = _existing_connection;
createThread();
}
void QMyThreadManager::createThread() {
if (threadRunner.isNull() == true) {
if (existing_connection == NULL) {
threadRunner = new QMyThreadInstance(NULL);
} else {
// this use to be done by the allocating class
existing_connection.setParent(NULL); // ensure no parent is set!
existing_connection.moveToThread(&actualThread); // move it to the
new thread
threadRunner = new QMyThreadInstance(existing_connection,NULL);
}
}
if (threadRunner.isNull() == false) {
existing_connection = NULL; // it's been handed to the object if we had
it
// connect object's signals to our signals
// connect our signals to object's slots
threadRunner->moveToThread(&actualThread); // move it to the new thread
actualThread.start(); // run the new thread
}
}
More information about the Qt-interest-old
mailing list