c++multithreadingqtthreadpoolqeventloop

Execute slots inside a QThreadPool


I have a class that should run in a thread and needs an event loop for the slots, currently I run it nicely with moveToThread(), but I'd like to use QThreadPool and I have encountered a problem.

When run with QThreadPool the run() method of my runnable is called from a pooled thread (I check this with QThread::currentThread()), but my slots aren't running in the pooled thread, so I think the object isn't moved to a thread in the pool.

I think this because I know the slots are run in the receiver's thread, which is exactly the (correct) behaviour I get when using the moveToThread() method and a QThread.

How do I get my QRunnable (Foo in the example below) to be run entirely in the pooled threads? Or is it something I'm doing wrong or understood wrong?

The following POC demonstrates the problem:

foo.h

#ifndef FOO_H
#define FOO_H

#include <QObject>
#include <QRunnable>
#include <QEventLoop>

class Foo : public QObject, public QRunnable
{
    Q_OBJECT
public:
    explicit Foo(int data, QObject *parent = 0);
    void run();
signals:
    void startWorking();
public slots:
    void doWork();

private:
    QEventLoop eventLoop;
    int data;
};

#endif // FOO_H

foo.cpp

#include "foo.h"

#include <QThread>
#include <QDebug>

Foo::Foo(int d, QObject *parent) :
    QObject(parent), eventLoop(this), data(d)
{
}

void Foo::run()
{
    qDebug() << "run() in: " << QThread::currentThread();
    connect(this, SIGNAL(startWorking()), this, SLOT(doWork()));
    emit startWorking();
    eventLoop.exec();
}

void Foo::doWork()
{
    qDebug() << "doWork() in: " << QThread::currentThread();
}

main.cpp

#include <QCoreApplication>
#include <QThreadPool>

#include "foo.h"

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Foo *foo = new Foo(42);

    QThreadPool::globalInstance()->start(foo);

    return a.exec();
}

Please note, however, that in my real code the signal won't be emitted right away, because it will be after I receive some data on the network.

PS: The POC can also be found here.


Solution

  • Maybe you could split your logic in class Foo into two:
    the hoster QRunnable with a QEventLoop, and a worker QObject, which you create on the worker thread in run() before calling QEventLoop::exec method. Then you forward all the signals to the worker object. So now your slots will be called on the pooled thread.
    However, QThreadPool is designed for executing lots of short tasks without creating too many simultaneous threads. Some tasks are enqueued and are waiting for others to finish. If this is not your intention, you might want to go back to good old QThread and use it instead.