Why is QThread::finished signal not being emitted?

12,788

Signal finished() gets emitted of cause, but you don't catch it.

Here:

connect(_thread, SIGNAL(finished()), this, SLOT(OnFinished()));

Qt::QueuedConnection is used, as _thread and this (service) are in different threads.

By the time finished() is emitted, _thread's event loop already finished executing, so signal will not be delivered to the slot.

You can explicitly use Qt::DirectConnection.

EDIT:

QTherad works like this:

QThread::start()
{
    emit started();
    run();
    emit finished();
}

QThread::run()
{
     eventloop->exec();
}

So, by the time finished is emitted, eventloop already stop execution. And as you move service to _thread, service's event loop is _thread event loop.

Note, that QObject itself has no its own event loop. Event loops are created by dialogs, threads and application.


Actually I will recommend in your simple case just use QtConcurent::run, as you do not perform actual event processing in the new thread, but just run single function.

Share:
12,788
Kiril
Author by

Kiril

CEO and Co-Founder of ST6.io E-mail: click to reveal e-mail

Updated on June 04, 2022

Comments

  • Kiril
    Kiril almost 2 years

    I've created my own TestService which runs on a separate QThread, but when the MainLoop terminates the QThread::finished signal does not get emitted. I saw a similar question, but the problem was slightly different there because the OP was overloading QThread whereas I simply move my class to the thread.

    Note that I do not overload the QThread class, I only overload QObject based on this example: http://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/

    Here is my TestService class:

    #include <QObject>
    #include <QThread>
    #include <QMutex>
    #include <QWaitCondition>
    #include <iostream>
    
    using namespace std;
    class TestService: public QObject
    {
        Q_OBJECT;
    private:
        volatile int _count;
        QWaitCondition _monitor;
        QMutex _mutex;
        QThread* _thread;
    
    public:
        TestService(int numSeconds)
        {
            _count = numSeconds;
            _thread = NULL;
            cout << "TestService()" << endl;
        }
    
        virtual ~TestService()
        {
            cout << "~TestService()" << endl;
        }
    
        void Start()
        {
            QMutexLocker locker(&_mutex);
            if(_thread == NULL)
            {
                _thread = new QThread;
    
                // Move this service to a new thread
                this->moveToThread(_thread);
    
                // The main loop will be executed when the thread
                // signals that it has started.
                connect(_thread, SIGNAL(started()), this, SLOT(MainLoop()));
    
                // Make sure that we notify ourselves when the thread
                // is finished in order to correctly clean-up the thread.
                connect(_thread, SIGNAL(finished()), this, SLOT(OnFinished()));
    
                // The thread will quit when the sercives
                // signals that it's finished.
                connect(this, SIGNAL(Finished()), _thread, SLOT(quit()));
    
                // The thread will be scheduled for deletion when the 
                // service signals that it's finished
                connect(this, SIGNAL(Finished()), _thread, SLOT(deleteLater()));
    
                // Start the thread
                _thread->start();
            }
        }
    
        void Stop()
        {
            _count = 0;
            _monitor.wakeAll();
        }
    private slots:
        void MainLoop()
        {
            cout << "MainLoop() Entered" << endl;
            while(_count > 0)
            {
                cout << "T minus " << _count << " seconds." << endl;
    
                QMutexLocker locker(&_mutex);
                _monitor.wait(&_mutex, 1000);
                _count--;
            }
            cout << "MainLoop() Finished" << endl;
            emit Finished();
        }
    
        virtual void OnFinished()
        {
            cout << "OnFinished()" << endl;
        }
    signals:
        void Finished();
    };
    

    Here is the testing code:

    void ServiceTest()
    {
        cout << "Press q to quit." << endl;
        cout << "Press s to start." << endl;
        cout << "Press t to stop." << endl;
        QSharedPointer<TestService> testService(new TestService(10));
        char in = 'a';
        while( in != 'q' )
        {
            switch(tolower(in))
            {
            case 's':
                testService->Start();
                break;
            case 't':
                testService->Stop();
                break;
            default:
                break;
            }
            cin.get(in);
            in = tolower(in);
        }
    }
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        ServiceTest();
    
        QTimer::singleShot(0, &a, SLOT(quit()));
    
        return a.exec();
    }
    

    The output is:

    Press q to quit.
    Press s to start.
    Press t to stop.
    TestService()
    s
    MainLoop() Entered
    T minus 10 seconds.
    T minus 9 seconds.
    T minus 8 seconds.
    t
    MainLoop() Finished
    q
    ~TestService()
    Press any key to continue . . .
    

    Could anybody explain why is finished not being emitted how I can fix it?