[Interest] QtConcurrent and event-driven objects

Dmitriy Purgin dpurgin at gmail.com
Mon Jan 26 11:56:41 CET 2015


Hi,

I'm using Qt to power an application server with multithreaded TCP listener
and maintenence tasks running in separate threads. The TCP listener runs in
main thread and spawns a separate thread to handle socket operation. The
socket is being created when the thread is running using a socket
descriptor passed from the TCP listener. The maintenance threads hold a
timer only and run on timer shot to query DB and do stuff (doesn't really
matter).

The thing is, I have always used it like this:

// [L1] Listing 1
// this simplified sample code neglects cleanup and possible memory leaks
class MaintenanceWorker : public QObject
{
    Q_OBJECT

public:
    MaintenanceWorker()
        : QObject(NULL),
          mTimer(NULL)
    {
        // executes in main thread
        moveToThread(&mThread);
        connect(&mThread, SIGNAL(started()), this, SLOT(onThreadStarted()));
    }
private slots:
    void onThreadStarted()
    {
        // executes in spawned thread
        mTimer = new QTimer();
        connect(mTimer, SIGNAL(timeout()), this, SLOT(onTimerTimeout()));
        mTimer->start(60000);
    }

    void onTimerTimeout()
    {
        // executes in spawned thread
    }
private:
     QThread mThread;
     QTimer* mTimer; // to be created in thread
};

After porting the project from Qt 4 to Qt 5 (using Qt 5.3 now) I've decided
to use high-level QtConcurrent facilities and namely QThreadPool for its
ability to reuse threads, control pool size and so on. So the sample code
above was transformed into this:

// [L2] Listing 2
// this simplified sample code neglects cleanup and possible memory leaks
class MaintenanceWorker : public QObject, public QRunnable
{
    Q_OBJECT
public:
    MaintenanceWorker()
        : QObject(NULL),
          mTimer(NULL)
    {
        // executes in main thread
    }

    void run()
    {
        // executes in spawned thread
        mTimer = new QTimer();
        connect(mTimer, SIGNAL(timeout()), this, SLOT(onTimerTimeout()));
        mTimer->start(60000);

        QEventLoop loop;
        loop.exec();
    }

    void onTimerTimeout()
    {
        // executes in main thread (!!!)
    }
private:
     QTimer* mTimer; // to be created in thread
};

// ...
QThreadPool threadPool;
threadPool.start(new MaintenanceWorker());

So the problem is that onTimerTimeout() slot runs in main thread (checked
with QThread::currentThread()). After reading the docs carefully -- which
had to be the first thing to do, my bad -- I've found that [cite1]:

   - *The child of a QObject <http://doc.qt.io/qt-5/qobject.html> must
   always be created in the thread where the parent was created.* This
   implies, among other things, that you should never pass the QThread
   <http://doc.qt.io/qt-5/qthread.html> object (this) as the parent of an
   object created in the thread (since the QThread
   <http://doc.qt.io/qt-5/qthread.html> object itself was created in
   another thread).
   - *Event driven objects may only be used in a single thread.*
   Specifically, this applies to the timer mechanism
   <http://doc.qt.io/qt-5/timers.html> and the network module
   <http://doc.qt.io/qt-5/qtnetwork-module.html>. For example, you cannot
   start a timer or connect a socket in a thread that is not the object's
   thread <http://doc.qt.io/qt-5/qobject.html#thread>.

It turns out that in case of [L2] MaintenanceWorker's thread is main thread
but in case of [L1] MaintenanceWorker's thread is the created QThread
thanks to moveToThread() call. Could anyone please tell if my conclusions
are true? If so, it's perfectly fine with me since I've found the
explanation in the docs but is there any feasible way of using timers
inside QRunnables started with QThreadPool? I've taken a quick look at
QThreadPool sources and didn't find anything moving the runnable to thread.
This can be worked around if connection type is explicitly set to
DirectConnection when connecting timer slot but is it a good practice?

Another important thing for me is network. The docs also specify that the
network module should also follow single thread policy. Does that mean that
an instance of QTcpSocket can't be used in QRunnable started by
QThreadPool?

Thanks

Cheers
Dmitriy

[cite1]
http://doc.qt.io/qt-5/threads-qobject.html#signals-and-slots-across-threads
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/interest/attachments/20150126/8a78ce6f/attachment.html>


More information about the Interest mailing list