Почему мои сигналы не запускают слоты без Qt :: DirectConnection?

#c #multithreading #qt

#c #многопоточность #qt

Вопрос:

У меня есть основной класс, который запускает поток, выполняющий одно действие. Я пытаюсь уловить, когда завершаются операции с потоком.

Основной класс:

 // .h
class MainClass : public QObject
{
    Q_OBJECT
public:
    QThread thread;
// ...
public slots:
    void onFinish();
}

// .cpp
void MainClass::startThread()
{
    thread = new QThread();
    worker = new Worker();
    worker->moveToThread(thread);

    connect(thread, SIGNAL(started()), worker, SLOT(process()));
    connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
    connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));

    connect(worker, SIGNAL(finished()), this, SLOT(onFinish()));
    // connect(worker, SIGNAL(finished()), this, SLOT(onFinish()), Qt::DirectConnection);
}

void MainClass::onFinish()
{
    std::cout << "Finished!" << std::endl << std::flush;
}
  

Worker Класс для потока:

 // .h
class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker(QObject *parent = 0);
    ~Worker();
public slots:
    void process();
signals:
    void finished();
}

// .cpp
void Worker::process()
{
    // ...Do stuff...

    emit finished();
}
  

Выполнение startThread не выводит «Готово!». Почему?

Я заметил, что если я добавляю Qt::DirectConnection в строку, которая подключается к onFinish() (например, в строке с комментариями), печатается сообщение. Но что я могу сделать, если я хочу выполнить onFinish() действия в потоке основного класса?

Редактировать 1

Кроме того, кажется, что finished() -> quit() connect также не работает, потому что, если я вызываю thread->isFinished() или thread->isRunning() , после сна в основном потоке, чтобы убедиться, что задача потока завершена, я получаю false и true , соответственно.

Редактировать 2

Поскольку это также может иметь значение, вот main.cpp:

 int _tmain(int argc, _TCHAR* argv[])
{
    QCoreApplication a(argc, argv);

    std::unique_ptr <MainClass> mc = std::make_unique <MainClass>();

    mc->startThread();
    mc->thread->wait();

    return a.exec();
}
  

Комментарии:

1. Вы пробовали без подключения к deleteLater() слотам? Если он запускает ваш onFinish() слот, вы можете вызвать deleteLater() оба объекта оттуда.

2. Вы можете подумать о том, чтобы поставить connect(worker, SIGNAL(finished()), this, SLOT(onFinish())); перед подключением к deleteLater() слотам.

3. Имеет ли поток, в котором MainClass создается экземпляр, активный цикл событий? Если нет, то сигналы в очереди приниматься не будут.

4. @LoPiTaL, @mfreiholz: Я попробовал ваши предложения. Сообщение все еще не напечатано. Также смотрите мою правку об isFinished() и isRunning() . Похоже, проблема не связана deleteLater() с.

5. @G.M. Я создаю экземпляр MainClass в своем «main.cpp «, после запуска a QCoreApplication . Означает ли это, что у меня есть цикл событий, который предотвращает получение сигналов?

Ответ №1:

На первый взгляд я бы сказал, что порядок соединений важен. deleteLater() Слоты всегда должны быть последними методами.

 connect(thread, SIGNAL(started()), worker, SLOT(process()));
connect(worker, SIGNAL(finished()), this, SLOT(onFinish()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
  

Sitenode — QRunnable
Вместо этого вы можете использовать QRunnable, который предназначен для коротких задач / заданий. Вы можете объединить их с QObject или QFuture / QFutureWatcher, чтобы получать уведомления, как только они будут завершены.

Обновление 1

Ваш цикл основных событий никогда не запускается, и похоже, что вы хотите выйти из приложения после завершения задачи. Я не думаю, что это хорошая реализация, но эти модификации должны работать для вас:

 int _tmain(int argc, _TCHAR* argv[])
{
    QCoreApplication a(argc, argv);

    std::unique_ptr<MainClass> mc = std::make_unique<MainClass>();
    mc->startThread();

    // do not wait!
    // let the main-event-loop handle events -> a.exec()
    // and quit() application by signal/slot

    return a.exec();
}

void MainClass::startThread()
{
    thread = new QThread();
    worker = new Worker();
    worker->moveToThread(thread);

    connect(thread, SIGNAL(started()), worker, SLOT(process()));
    connect(worker, SIGNAL(finished()), this, SLOT(onFinish()));
    connect(worker, SIGNAL(finished()), thread, SLOT(quit()));
    connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    connect(thread, SIGNAL(finished()), qApp, SLOT(quit()));
}
  

Комментарии:

1. Спасибо. Я решил свою проблему, хотя до сих пор не уверен на 100%, в чем была проблема… Я думаю, что G.M. был прав насчет активного цикла событий, препятствующего выполнению слотов, поэтому проблема заключалась не в порядке «подключения». Я изучил вашу идею использования QRunnable и столкнулся с аналогичной проблемой. Наконец, я нашел решение с QFuture помощью и QtConcurrent::run . Но я не могу понять, почему последнее решение может сработать, если другие этого не сделали…