Using QThread and moveToThread properly with QTimer and QTcpSocket

Class Instances are created on the calling thread. QTimer inherits QObject. Each Thread on Qt can have an event loop if it calls exec(). so you want to move QTimer to an event loop on another thread. so you should manually move it.

Therefore, delay their creation until after you move the object: -

class CommsHandlerIP : public QObject
{
    Q_OBJECT

    public slots:
       void Initialise();

    private: 
       void Run();

       // c++ 11, initialising in headers...
       QTimer* m_pTimer = NULL;
       QTcpSocket* m_pSocket = NULL;   
};

void CommsHandlerIP::Initialise()
{
     m_pTimer = new QTimer(this);
     m_pSocket = new QTcpSocket(this);

     Run();
}

QThread m_commsThread;
m_pICommsHandler = new CommsHandlerIP();

// Note Qt 5 connect style
connect(&m_commsThread, &QThread::started, m_pICommsHandler, &CommsHandlerIP::Initialise);
m_pICommsHandler->moveToThread(&m_commsThread);
m_commsThread.start();

When the thread is started, the CommsHanderIP Initialise function is called; this is where you should create and setup the QTcpSocket and QTimer objects before calling Run(). As the CommsHandlerIP is running in the new thread before creating those objects, they will also share the same thread affinity.


There is a much simpler method of achieving all this that follows the same algorithm but doesn't involve all the boilerplating needed to create threads and changing thread affinities, using QRunnable and QThreadPool

If I convert Merlin069's example, you'll see how it simplifies the code a bit:

class CommsHandlerIP : public QObject, public QRunnable
{
    Q_OBJECT
    public:
       void run();

    public slots:
        //... any slots

    signals:
        //... any signals

    private:
       // c++ 11, initialising in headers...
       QTimer* m_pTimer = NULL;
       QTcpSocket* m_pSocket = NULL;   
};

void CommsHandlerIP::run()
{
     m_pTimer = new QTimer();
     m_pSocket = new QTcpSocket();

     //...

     delete m_pTimer;
     delete m_pSocket;
}

QThreadPool::globalInstance()->start(new CommsHandlerIP);

I stumbled across this when searching on Timer behaviour and movetoThread. The accepted answer is a good work-around but not really the root cause of the problem. There is a general rule that when you move an object then all child objects will move as well. So you just need to make sure that the QTimer becomes a child so pass the this pointer in its constructor.

CommsHandlerIPL::CommsHandlerIP()
: QObject(), m_pTimer(new QTimer(this))        // <=== crucial to make it a "child" object 
{
}