QThread не останавливается/не обрабатывает сигнал

Я пытаюсь выполнить некоторую работу в отдельном потоке и остановить этот поток, когда работа будет выполнена.

Я установил такое соединение

thread.connect( workerClass, SIGNAL( finished() ), SLOT( quit() ) );

но слот quit() никогда не вызывается, когда я испускаю сигнал finish(). В командной строке не отображаются предупреждения, связанные с неудачными подключениями.

Я создал простой тестовый проект, чтобы сузить проблему, и я получаю такое же поведение:

ТестКласс.h:

#ifndef TESTCLASS_H
#define TESTCLASS_H
class TestClass : public QObject
{
    Q_OBJECT
public:
    TestClass() { }
signals:
    void finished();
public slots:
    void doWork();
}
#endif // TESTCLASS_H

ТестКласс.cpp:

#include "TestClass.h"
#include <QtCore/QDebug>
void TestClass::doWork()
{
    qDebug() << "Starting";
    for( long i = 0; i < 1000000000; i++ );
    qDebug() << "Done";
    emit finished();
};

и main.cpp:

#include <QtCore/QCoreApplication>
#include <QtCore/QThread>
#include "TestClass.h"

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

    TestClass testClass;
    QThread testThread;

    testClass.moveToThread( &testThread );

    testThread.connect( &testClass, SIGNAL( finished() ), SLOT( quit() ) );
    testClass.connect( &testThread, SIGNAL( started() ), SLOT( doWork() ) );

    testThread.start();
    testThread.wait();

    return 0;
}

Итак, вот что я ожидал:

  1. После обработки testThread.start() вызывается функция run() потока, которая внутренне вызывает exec() и запускает цикл обработки событий. Кроме того, он выдает сигнал started().
  2. testClassвызывается doWork() слот.
  3. Когда TestClass::doWork() выполнено, выдается сигнал finished().
  4. Цикл событий testThread получает сигнал и перенаправляет его в свой слот quit().
  5. testThread.wait() возвращается из ожидания, так как поток завершен.

Шаги 1-3 работают, и я получаю вывод qDebug(). Проблема на шаге 4: сигнал запускается и перенаправляется в какой-то цикл событий (я немного отладил его, но не смог понять, в какой цикл событий он был запущен), но слот quit() никогда не вызывается.

Что я делаю неправильно?

PS: Пожалуйста, не предлагайте вместо этого создавать подклассы QThread, весь смысл того, что я пытаюсь сделать это, заключается в том, что я хочу избежать создания подклассов QThread.


person Tim Meyer    schedule 15.09.2011    source источник


Ответы (2)


Я считаю, что проблема в том, что на самом деле у вас нет цикла событий Qt, работающего в сценарии. Если вы замените testThread.wait() на a.exec() (который запускает цикл событий Qt), вы должны увидеть, что ваши сигналы/слоты обрабатываются правильно.

Если вы хотите, чтобы приложение завершило работу после завершения вашего потока, вы можете использовать для этого слот QCoreApplication quit().

person Chris    schedule 15.09.2011
comment
Да, я думаю, ты прав. Ему лучше сделать int ret = a.exec(); testThread.wait(); return ret;. - person RedX; 15.09.2011
comment
У потоков по умолчанию есть собственный цикл событий, поэтому я подумал, что этого будет достаточно. Оказывается, они ничего не стоят, если основной цикл событий не запущен. Вы, ребята, правы. - person Tim Meyer; 16.09.2011
comment
QThread имеет цикл обработки событий, только если вы вызываете в нем int QThread::exec(). Цикл событий — это, по сути, обработка очереди в цикле while. Нет цикла, который обрабатывает события и сигналы => сигналы не поступают. - person Marcel Blanck; 19.11.2013
comment
Начиная с Qt 4.4 или чего-то подобного, если вы не создаете подкласс QThread, он по умолчанию вызывает exec() внутри run(). Проблема здесь заключалась в отсутствующем цикле основного события. - person Tim Meyer; 21.11.2013

У меня была такая же проблема, и я нашел ответ. Вот мой вопрос: Какая польза от функции QThread.wait() ?

Чтобы решить вашу проблему, вам не нужно запускать a.exec(), вам нужно вместо вызова quit(), который пытается отправить сигнал в цикл событий вашего приложения (что, по-видимому, не существует), вы должны вызвать его напрямую.

Итак, для вашего кода отпустите строку:

testThread.connect( &testClass, SIGNAL( finished() ), SLOT( quit() ) );

И вместо:

emit finished();

Использовать:

this->thread()->quit();

Тада... проблема решена. Извлеченный урок: не пытайтесь выйти из рабочего потока с помощью механизма qt signal-slot изнутри него, потому что ваши сигналы не заканчиваются там, где они должны быть (цикл событий вашего рабочего потока), но они заканчиваются в создании вместо этого нить. Вы никогда не знаете, что делает этот поток, и работает ли его цикл событий или нет, и в любом случае это не должно иметь значения для вашего рабочего потока... Вместо этого вызовите quit напрямую.

person Ali B    schedule 19.11.2013