QObject::moveToThread() with child objects! [SOLVED]



  • Hey there,

    I have a thread class ComThread deriving from QThread just implementing the void run() method.

    @void run()
    {
    exec()
    }@

    Now I have an init() function called in main() that creates a comThread object and some objects by parsing a config file.
    One of these objects is a Connection.
    A connection defines an interface for sending/receiving data independent of the protocol.
    The Connection class holds either a QTcpSocket or QTcpServer (depends on the config).
    Once the connection object is instantiated, connection->moveToThread(comThread) is called.
    At the end of init() i call comThread->start()

    Now the problem:
    If I start the application and i try to send something via the connection, QT returns the following:
    QObject::connect: Cannot queue argument of type 'QByteArray&'
    (Make sure 'QByteArray' is registered using qRegisterMetaType() ).

    The QByteArray& is related to the received(QByteArray&) signal emitted by the connection object.
    Why is that? Without moving the connection to the thread all works fine!

    Now the second problem:
    Connection is derived from QObject
    The Connection constructor for the server connection looks like that:

    @
    Connection::Connection(QObject* parent): QObject(parent)
    {
    _socket = new QTcpServer(this); //if i change this to parent no error occurs
    }@

    When i start the program and the connection is moved to the thread Qt returns:
    QObject: Cannot create children for a parent that is in a different Thread

    ???

    Thanks for every answer!



  • Hi! Regarding the second problem:
    "The official documentation":http://qt-project.org/doc/qt-4.8/qobject.html#moveToThread says that you can’t use QObject::moveToThread() on an object which has a parent - maybe its the cause of your problem?



  • Yes, but it also says: Changes the thread affinity for this object and its children
    My Connection does not have a parent. It is 0.

    But it is complaining!

    QObject: Cannot create children for a parent that is in a different Thread
    It says, that the complaining object's parent is QTCPSocket which means that it goes deeper down than i can handle!?

    Here is the tree:
    @Connection - Parent is 0
    |- QTcpSocket(Parent is Connection)
    |- QTcpServer(Parent is Connection)@

    What am I doing wrong?



  • This is an excellent example for the pitfalls of subclassing from QThread.
    Read these article:
    "You're doing it wrong":http://blog.qt.digia.com/blog/2010/06/17/youre-doing-it-wrong/
    "best practise for QThread":http://qt-project.org/wiki/QThreads_general_usage
    "doc note in QThread reference":http://qt-project.org/doc/qt-4.8/QThread.html#notes



  • I am a bit confused,
    "Best practise for QThread":http://qt-project.org/wiki/QThreads_general_usage warns that we should NEVER allocate heap objects (using new) in the constructor of the QObject worker(to be moved into another thread) while "the official docs":http://qt-project.org/doc/qt-4.8/qobject.html#moveToThread says that moveToThread changes the thread affinity for this object and its children.
    In another "forum thread":http://qt-project.org/forums/viewthread/22200/ a guy allocated timer(using new) in worker constructor and everithing worked fine.
    Could someone explain this please?



  • If you create objects on the heap in your worker objects constructor and do not make them children of the worker object then they are not moved. If they are children of the worker object then you should be fine as far as I understood.

    I think the advise in the best practise article is mistakenly taken from experiences with subclassing QThread were it is true that you should not create heap objects in the constructor but only in the run functions implementation (that is unless you call moveToThread() on your own QThread subclass and the heap objects are children of the QThread subclass).

    I know it is confusing at first, but believe me the worker object approach will make your life a whole lot easier. Just remember to only use signal-slot communication between threads.

    For simple tasks which are in a fire and forget kind of manner or which easily fit in one function you can also consider using "QRunnable":http://qt-project.org/doc/qt-4.8/qrunnable.html or "QtConcurrent":http://qt-project.org/doc/qt-4.8/qtconcurrent.html.



  • KA51O, thanks for the clarification. Your summary is probably the safest, I will stick to it.

    Seraph, could you please keep us updated on your progress :)



  • Yes I will as soon as possible! Thanks for the answers so far!



  • Hi there.

    OK. I read all the articles and i think i got QThreads right from the beginning on.
    My problem has another much deeper root.

    I made up a sand box code:
    @

    class BasicThread: public QThread
    {
    public:
    BasicThread(QObject* parent=0): QObject(parent){ }
    protected:
    virtual void run() { exec(); }
    };

    int main(int argc, char* argv[])
    {
    QApplication *a = new QApplication(argc, argv);

    BasicThread* thread = new BasicThread();

    QTcpServer* socket = new QTcpServer();

    socket->moveToThread(thread);

    socket->listen(QHostAddress::Any, 1234);

    thread->start();

    return a->exec();
    }
    @

    Now QT streams the following error:
    QObject: Cannot create children for a parent that is in a different thread!
    (Parent is QTcpServer ...) ...

    OK. So far so bad.
    QTcpServer::listen() seems to create a new object! Which would make sense!
    If i remove the line: @socket->listen(QHostAddress::Any, 1234);@
    the error wont appear!

    My solution for now is to catch the started() signal of the thread and call the line in the catching slot. But this makes things very complicated for me! I would have to give all new generated sockets to a worker (in a list) and the process() or work() method would call listen() of each socket in this list. That also means, that i need a worker for each different class which will be instanciated in my init method (see first post).

    Any ideas to do that easier with QT?



  • [quote author="Seraph" date="1354789060"]
    @
    class BasicThread: public QThread
    {
    public:
    BasicThread(QObject* parent=0): QObject(parent){ } // <------ why not QThread(parent) ?
    protected:
    virtual void run() { exec(); } // this is already QThreads default implementation
    };
    @
    [/quote]

    Why are you deriving from QThread and then only use QObjects constructor for the constructor of your subclass?

    Also you don't need to subclass QThread at all for your use case. This would be sufficient:
    @
    int main(int argc, char* argv[])
    {
    QApplication *a = new QApplication(argc, argv);

    QThread* thread = new QThread(a);

    QTcpServer* socket = new QTcpServer();

    socket->moveToThread(thread);

    connect(thread, SIGNAL(finished()), socket, SLOT(deleteLater()));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

    socket->listen(QHostAddress::Any, 1234);

    thread->start();

    return a->exec();
    }
    @



  • Hi KA510,

    First of all, sorry! It is ofcourse a mistake: It should be QThread not QObject (Would not even compile i guess)

    I derive the QThread class because of run() calling exec(). If i don't, the application will crash. This is when i do not use QThread(a)!

    The problem is still there! KA510 did you test your code? Did you get the same result and error message?

    Thanks for your effort



  • Sry I didn't test the code, that was just brain to terminal. Did you debug the test app? Where does it crash and what's the call stack like? It's just a guess but maybe this has to do with the fact that the main eventloop only starts after the a->exec() call.
    I did something very similar in different applications and it always worked just fine.



  • The crash is not my problem! The problem is still the fact that i cannot call the listen() method of QTcpServer without getting the error
    QObject: Cannot create children for a parent that is in a different thread!
    (Parent is QTcpServer …) …

    Because listen() appears to create a new QObject inside which tries to set the QTcpServer instance as parent!???



  • Try implementing a wrapper for the QTcpServer. Something like this:
    @
    class MyServer : public QObject
    {
    Q_OBJECT
    public:
    MyServer(QObject* a_parent);

    public slots:
    void startServer();
    void stopServer();
    void handleNewConnection();
    signals:
    void newConnectionSg(QTcpSocket* a_pSocket);
    private:
    QTcpServer* m_pServer;
    };

    MyServer::MyServer(QObject* a_parent)
    : QObject(a_parent)
    {
    m_pServer = new QTcpServer(this);
    connect(m_pServer, SIGNAL(newConnection()), this, SLOT(handleNewConnection()));
    }

    void MyServer::handleNewConnection()
    {
    emit newConnectionSg(m_pServer->nextPendingConnection());
    }

    void MyServer::startServer()
    {
    m_pServer->listen(QHostAddress::Any, 1234);
    }

    void MyServer::stopServer()
    {
    m_pServer->close();
    }
    @
    Then do this.
    @
    int main(int argc, char* argv[])
    {
    QApplication *a = new QApplication(argc, argv);

     QThread* thread = new QThread(a);
     
     MyServer* server = new MyServer();
     
     server->moveToThread(thread);
     
     connect(thread, SIGNAL(started()), server, SLOT(startServer()));
     connect(thread, SIGNAL(finished()), server, SLOT(stopServer()));
     connect(thread, SIGNAL(finished()), server, SLOT(deleteLater()));
     connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
     
     QTimer::singleShot(1000, thread, SLOT(start()));
     
     return a->exec(&#41;;
    }
    

    @

    BUT it really isn't neccessary to move the QTcpServer to another thread because of its asynchronous interface. What you could do is move the sockets that QTcpServer::nextPendingConnection() returns into threads of their own (again not neccessary for simple use cases because of asynchronous design).



  • In my framework i already got the sockets wrapped.
    I also tried the same thing with QTcpSocket and connectToHost()
    Same result.

    But i now found a way to get rid of the error!

    I'm now calling listen() before calling move to thread!

    @
    int main(int argc, char* argv[])
    {
    QApplication *a = new QApplication(argc, argv);

    QThread* thread = new QThread(a);

    QTcpServer* socket = new QTcpServer();

    QObject::connect(thread, SIGNAL(finished()), socket, SLOT(deleteLater()));
    QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

    socket->listen(QHostAddress::Any, 1234);

    socket->moveToThread(thread); //this line has to come AFTER calling socket->listen()

    thread->start();

    return a->exec();
    }@

    I am testing the full functionality right now.



  • Just out of curiosity have you tried calling listen(..) after starting the thread ?



  • Calling listen AFTER staring the thread also worked!



  • Hmm must have got something to do with thread context I guess. If you call it before moveToThread it's executed in the context of the main thread (running eventloop) if you call it after starting your new thread it's executed in the new threads context (also running eventloop). If you call it in between moveToThread() and start() it's already in the new threads context but the eventloop of this thread is not running yet.

    Just a guess though. I would really like to know the exact reason.



  • Yes me, too.

    Ok. For the QTcpServer it works fine. The QTcpSocket gives me an error when sending/receiving stuff:
    QSocketNotifier: socket notifiers cannot be enabled from another thread (????)

    What does that mean at all? Beside this error it is all working fine! It is sending and receiving and signals are sent and received!



  • OK. It is by the way senseless to put a socket into a thread!
    I am now using the thread-worker approach and it is working perfectly. If a signal is connected to a QObject (the worker) which is in another thread (one can use QObject::moveToThread()) the signal will be appended to a queue. The thread worker will receive the signals when the previos signal has been proceeded completely!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.