Home > front end >  Qt GUI hanging with worker in a QThread
Qt GUI hanging with worker in a QThread

Time:01-10

I have a worker which needs to complete an arbitrary blocking task

class Worker : public QObject
{
    Q_OBJECT;

public:
    using QObject::QObject;

public slots:
    void start()
    {
        for (int i = 0; i < 10; i  )
        {
            qDebug("I'm doing work...");
            Sleep(1000);
        }
    }
};

In my MainWindow constructor, I start the task like so:

class MainWindow : public QWidget
{
    Q_OBJECT;

public:
    explicit MainWindow(QWidget* parent = nullptr) : QWidget(parent)
    {
        QThread* t = new QThread(this);
        Worker* w = new Worker(this);
        w->moveToThread(t);

        this->connect(
            t, &QThread::started,
            w, &Worker::start
        );
        this->connect(
            t, &QThread::finished,
            t, &QThread::deleteLater
        );

        t->start();
    }
};

And my entry point:

int main(int argc, char** argv)
{
    QApplication app(argc, argv);

    MainWindow mainWindow{};
    mainWindow.show();

    return app.exec();
}

When I run the program, I just get a spinning mouse circle, and the window contents don't load until 10 seconds later (when the worker is complete). Why is this so? What do I need to do so that Worker::start is run in the background without affecting the GUI?

P.S. I am using Qt 6.2.2 and Windows 11, but I highly doubt that has anything to do with this issue.

CodePudding user response:

Simply:

class MainWindow : public QWidget
{
    Q_OBJECT;

public:
    explicit MainWindow(QWidget* parent = nullptr) : QWidget(parent)
    {
        QThread* t = new QThread(); // delete this
        Worker* w = new Worker(); // delete this
        w->moveToThread(t);

        this->connect(
            t, &QThread::started,
            w, &Worker::start
        );
        this->connect(
            t, &QThread::finished,
            t, &QThread::deleteLater
        );

        t->start();
    }
};

It gives a reasonable explanation.

CodePudding user response:

If you check your output, you're very likely to find a line like this:

QObject::moveToThread: Cannot move objects with a parent

To prove you can't, just add some debug lines:

QThread* t = new QThread(this);
Worker* w = new Worker( this);
w->moveToThread(t);

qDebug() << "CURRENT: " << QThread::currentThread();
qDebug() << "WORKER : " << w->thread();

Worker's thread is still the current one, after trying to move it into t ...

I would recommend to make both objects (QThread and Worker) private members of your MainWindow class: no reason to use pointers, here:

class MainWindow : public QWidget
{
    Q_OBJECT;

    QThread _thread;
    Worker _worker;

public:
    explicit MainWindow(QWidget* parent = nullptr) : QWidget(parent)
    {
        _worker.moveToThread(t);

        connect(
            &_thread, &QThread::started,
            &_worker, &Worker::start
        );

        _thread.start();
    }
};

CodePudding user response:

The problem is that the worker is actually not moved to thread (Isn't a warning written to console? I bet it is.), because its parent, i.e. MainWindow instance is still in the GUI thread.

When moving to thread, you can only move the whole hierarchy of objects by moving the very top parent. You cannot have parent in different thread than its children.

You should create the worker without parent.

Worker* w = new Worker();

Of course you should also add some finished() signal to your Worker class.

class Worker : public QObject
{
    Q_OBJECT;

public:
    using QObject::QObject;

    void start()
    {
        for (int i = 0; i < 10; i  )
        {
            qDebug("I'm doing work...");
            Sleep(1000);
        }
        emit finished();
    }

signals:
    void finished();
};

and add these connections:

this->connect(
            w, &Worker::finished,
            t, &QThread::quit
        ); 
this->connect(
            t, &QThread::finished,
            w, &Worker::deleteLater
        );

otherwise your thread's even loop will not end and the worker will not be deleted.

  •  Tags:  
  • Related