Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

QThread::wait() does not return without using direct connection

I got some trouble with Qt Threads and Connections. I found several tutorials and discussions on this topic, I followed this tutorial to create the thread. But I still got the problem, that calling wait() on the thread never returns and the UI freezes.

A similar question was asked here before (the second example): Qt connection type between threads: why does this work?

In the last edit of the question, the author mentions that he had created a deadlock. I assume, I do the same in my application. But I still do not understand, why this happens. Reading the suggested article did not help me understanding. I just got the point, that deadlocks can happen, but I don't know, what's causing it there or in my case.

I have also created an example that's reduced to the core problem. Find the code at the bottom of this question.

So my questions are: What exactly is the cause for the deadlock in my example? Is there a solution without making the connection a direct connection?

I'd really appreciate any hints.

Thanks!

EDIT:

Because of the comments I tried it to send the stop request via a signal and I added a QCoreApplication::processEvents() call in the thread loop. But the main problem is still the same.

EDIT2:

I found an acceptable solution, after thinking a bit more about event loops:

thread.requestStop();

// now instead of using wait(), we poll and keep the event loop alive
// polling is not nice, but if it does not take a very long time
// for the thread to finish, it is acceptable for me.
while (thread.isRunning())
{
    // This ensures that the finished() signal
    // will be processed by the thread object
    QCoreApplication::processEvents();        
}

This actually works and the worker itself controls how to stop working.

After coming up with this, I also have an explanation for the freezing issue: Calling wait seems to keep the main thread either busy or suspended so it does not process any events. Since the thread object lives in the main thread, the thread's finished() signal is enqued but never processed.

My implicit assumption, that thread.wait() would still keep the event loop working, was obviously wrong. But then, what's the QThread::wait() function good for?!?

This is just a theory, but maybe someone here can verify or falsify it...

EDIT 3 (final solution):

After reading this small article and implmenting a subclassing solution, I think that this is preferable for this particular proble. There is no need for an event loop and I'm fine with direct calls on a different thread and using mutex protection. It's less code, easier to understand and easier to debug.

I think I would use the non-subclassing strategy only, if there were more interaction with the thread than just start and pause.


My reduced Example

Maybe I should point out, that I do not delete the thread, because in my original application, I want to resume later, so stopping it actually means pausing it.

worker.h:

#ifndef WORKER_H
#define WORKER_H

#include <QObject>
#include <QMutex>

class Worker : public QObject
{
    Q_OBJECT

public:
    explicit Worker(QObject* parent = NULL);

public slots:
    void doWork();
    void requestStop();

signals:
    void finished();

private:

    bool stopRequested;
    QMutex mutex;
};

#endif // WORKER_H

worker.cpp:

#include "worker.h"

#include <QThread>
#include <iostream>

using namespace std;

Worker::Worker(QObject *parent)
    : stopRequested(false)
{
}

void Worker::doWork()
{
    static int cnt = 0;

    // local loop control variable
    // to make the usage of the mutex easier.
    bool stopRequesteLocal = false;

    while (!stopRequesteLocal)
    {
        cout << ++cnt << endl;
        QThread::msleep(100);

        mutex.lock();
        stopRequesteLocal = stopRequested;
        mutex.unlock();
    }

    cout << "Finishing soon..." << endl;

    QThread::sleep(2);
    emit finished();
}

void Worker::requestStop()
{
    mutex.lock();
    stopRequested = true;
    mutex.unlock();
}

main program:

#include <QCoreApplication>
#include <QThread>
#include <QtCore>
#include <iostream>

#include "worker.h"

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    QThread thread;
    Worker worker;


    QObject::connect(&thread, SIGNAL(started()), &worker, SLOT(doWork()));

    // this does not work:
    QObject::connect(&worker, SIGNAL(finished()), &thread, SLOT(quit()));

    // this would work:
    //QObject::connect(&worker, SIGNAL(finished()), &thread, SLOT(quit()), Qt::DirectConnection);

    // relocating the moveToThread call does not change anything.
    worker.moveToThread(&thread);

    thread.start();

    QThread::sleep(2);

    worker.requestStop();
    cout << "Stop requested, wait for thread." << endl;
    thread.wait();
    cout << "Thread finished" << endl;

    // I do not know if this is correct, but it does not really matter, because
    // the program never gets here.
    QCoreApplication::exit(0);
}
like image 579
Kanalpiroge Avatar asked Dec 17 '13 16:12

Kanalpiroge


1 Answers

I added my own answere to the question text as EDIT 3.

like image 92
Kanalpiroge Avatar answered Sep 17 '22 23:09

Kanalpiroge