Поток Qt не останавливается после вызова exit / quit - PullRequest
5 голосов
/ 20 февраля 2012

Я пытаюсь лучше понять сигналы и слоты Qt в сочетании с потоками. Итак, я попробовал это минимальное приложение:

foo.h:

#include <QObject>

class A : public QObject {
  Q_OBJECT

public:
  void doit();

signals:
  void x();
};

class B : public QObject {
  Q_OBJECT

public slots:
  void h();
};

foo.cpp:

#include "foo.h"

#include <QThread>
#include <QCoreApplication>

void B::h() {
  qDebug("[%d] B::h() here!", (int) QThread::currentThreadId());
  QCoreApplication::instance()->quit();
}

void A::doit() {
  qDebug("[%d] emitting...", (int) QThread::currentThreadId());
  emit x();
}

int main(int argc, char* argv[]) {
  QCoreApplication app(argc, argv);
  A a;
  B b;
  QObject::connect(&a, SIGNAL(x()), &b, SLOT(h()));
  QThread t;
  t.start();
  b.moveToThread(&t);
  a.doit();
  t.wait();
  return 0;
}

Все хорошо, только t.wait () в конце никогда не возвращается. Насколько я понимаю, вызов quit () должен остановить цикл обработки событий, что означает, что exec () должен вернуться и, следовательно, должен запустить () и выполнение потока должно быть остановлено. Я что-то упустил?

Ответы [ 2 ]

10 голосов
/ 20 февраля 2012

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

t.wait() никогда не вернется, потому что работающий поток постоянно ожидает событий.Чтобы остановить поток, вы должны позвонить QThread::quit () [slot]

Если вы хотите выйти из приложения после завершения работы, вы должны подать сигнал, который подключен к QCoreApplication::quit () [static slot]

Если выЕсли вы хотите остановить рабочий поток после выполнения работы, вы также должны подать сигнал, связанный с void QThread::quit () [slot]

Добавлено: Потоки, события и объекты QObjects для дальнейшего чтения

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

Из Qt QThread doc:

Каждый QThread может иметь свой собственный цикл обработки событий.Вы можете запустить цикл обработки событий, вызвав exec ();Вы можете остановить это, вызвав exit () или quit ().Наличие цикла событий в потоке позволяет подключать сигналы от других потоков к слотам в этом потоке, используя механизм, называемый соединения в очереди .Это также позволяет использовать в потоке классы, для которых требуется цикл обработки событий, например QTimer и QTcpSocket.Однако обратите внимание, что невозможно использовать какие-либо классы виджетов в потоке.

Doc для Qt :: QueuedConnection:

Слот вызывается, когда элемент управления возвращаетсяк циклу событий потока получателя.Слот выполнен в потоке получателя.

2 голосов
/ 20 февраля 2012

Похоже, что с вашим кодом много проблем.

  • Вы не звоните app.exec(). Это означает, что нет основного цикла событий. Сигнал x A не будет издан.
  • По умолчанию поток имеет свой собственный цикл в Qt (по крайней мере, несколько лет назад). Затем запуск потока вызывает Qthread::run(), и цикл событий запускается. Вот где ваша нить, а не в t.wait().
  • Какова цель t.wait()? Я полагаю, что вы злоупотребляете этим.
  • (Если все остальное было в порядке), в B::h() вы останавливаете основной поток из другого потока. Это то, что вы хотели сделать?

Так что мой первый совет будет добавить app.exec(), посмотрите, как он себя ведет. Объясните, что вы пытаетесь сделать, и перепишите что-нибудь еще. Потому что вы делаете это неправильно

...