[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