Creating a threaded QObject in QT

There are times when you need a worker thread in QT, that is more then just fired off to do work and forget. In the past, I have derived from QThread, implemented Run() and did the work I needed to do and exit. But there are times where you want to create a thread that can interact with QT like the main thread, handling events and slots, and do so asynchronously without being blocked or blocking the main thread. The process to do this is really not obvious, but its a pretty slick setup that is new to QT 4.4, and from I can tell the documentation is lacking and still showing common practice in having the user derive from QThread.

The steps I usually take to do this is create my object (QObject or QWidget derivitive) that I want to thread, and make the QThread a member of that class.

class CWorker : public QObject
{
Q_OBJECT
public:
    explicit CWorker(QObject *parent = 0);
    virtual ~CWorker();
private:
    QThread    * m_pThread;
};

My intention, is that I want my Worker object, that is going to be receiving events is going to live in the context of its member thread.

In the CTOR of CWorker, I setup the thread, and change the context of CWorker to this new thread.

CWorker::CWorker(QObject *parent) :
    QObject(parent)
{
    m_pThread = new QThread;
    m_pThread->start();
    moveToThread( m_pThread );
}

In the DTOR, I allow it to exit the thread, join it back and then delete it entirely. The premise is that QThread, without overriding Run(),  calls exec() and allows for normal typical execution of the object and slots its monitoring. Calling moveToThread() moves the event handling from the global event loop into the event loop within the new QThread.
CWorker::~CWorker()
{
    m_pThread->exit( 0 );
    m_pThread->wait();
    delete m_pThread;
}

So now, If I add a slot to CWorker, any code executed to that object will be executed on that objects thread. For example ….

CWorker

class CWorker : public QObject
{
Q_OBJECT
public:
    explicit CWorker(QObject *parent = 0);
    virtual ~CWorker();
public slots:
    void SayHello();
private:
    QThread        * m_pThread;
};

void CWorker::SayHello()
{
    qDebug() << "Er, Hello. My ThreadId is: ";
    qDebug() << QThread::currentThreadId();
}

MainWindow

void MainWindow::on_pushButton_clicked()
{
    qDebug() << "MainWindow ThreadId: ";
    qDebug() << QThread::currentThreadId();
    if( !m_pWorker )
        m_pWorker = new CWorker;
     QMetaObject::invokeMethod( m_pWorker, "SayHello", Qt::QueuedConnection );
}

I created a button on my mainwindow, that when pushed creates the worker thread on the first call (Lazy Instantiation). It then executes the method in its thread space via invokeMethod. The net result is code executing in two separate threads.


Starting /home/jimster/Foo/Foo...
MainWindow ThreadId:  3057285904
Er, Hello. My ThreadId is:  3055655792
MainWindow ThreadId:  3057285904
Er, Hello. My ThreadId is:  3055655792
/home/jimster/Foo/Foo exited with code 0

There I started the application, pressed my button twice, and then closed the application. I should refer to people who wish to read more on this subject to start at this blog entry from a QT author.

http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/

This entry was posted in Software. Bookmark the permalink.