Не проходит сигнал или не происходит соединения в Qt когда оба объекта соединения находятся в другом потоке

230
09 октября 2018, 16:20

Пишу небольшой сервер, где при нажатии кнопки создаётся новый поток (th) и там запускается TCP слушатель определённого порта. При каждом новом TCP подключении необходимо делать два новых коннекта объектов, оба которых созданы и работают в потоке (th).

std::thread th([this]() -> void {
    while (true) {
        std::unique_ptr<Bs> bs = std::make_unique<Bs>();
        bs->GetTcpServer()->listen(QHostAddress::Any, 9005);
        connect(bs->GetTcpServer(), SIGNAL(newConnection()), bs.get(), SLOT(ConnectionWasEstablished()), Qt::QueuedConnection);
        connect(bs.get(), SIGNAL(update_server_logs(QString)), this, SLOT(UpdateServerLog(QString)), Qt::QueuedConnection);
        while (!bs->IsConnected()) {  // Ждём следующего подключения.
            std::this_thread::sleep_for(100ms);
        }
        bs_list_.push_back(std::move(bs));
    }
});

Проблема в том, что при подключении TCP клиента до соответствующих слотов сигнал не доходит. Всё что я смог нагуглить - это то что скорее всего в новом потоке требуется свой «Event loop», чтобы мочь обрабатывать сигналы, но как его создать в новом потоке не понял.

Проблема решена:

Всё заработало когда я создал поток по статье которую нашёл Bearded Beaver. Добиться работоспособности с std::thread я так и не смог(. Большое спасибо всем кто помог, особенно Bearded Beaver и ixSci.

Answer 1

Добавьте в цикл QCoreApplication::processEvents();, должно помочь.

Вообще говоря, Ваш код ужасен. Используете Qt? Используйте его возможности, его потоки и всё, что с ними связано. Вот статья про то, как правильно работать с QThread: Threads Events QObjects, там же есть русский перевод.

Answer 2

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

Не создавайте подклассов QThread!

Используйте moveToThread() и "рабочий" класс-наследник от QObject.

Далее описано, как Вы должны реализовать многопоточную операцию, используя класс QThread.

Создайте класс-работник, который унаследован от класса QObject. Этот класс должен определять следующие сигналы и слоты:

  • Слот (слоты), в котором происходит работа.
  • Сигнал, который информирует вызывающий код о завершении работы.
  • Опционально, сигнал, который информирует вызывающий код в случае возникновения ошибок.

Класс-работник:

 class Worker : public QObject {
     Q_OBJECT
 public:
     explicit Worker(QObject * parent  = nullptr);
 public slots:
     void process();
 signals:
     void finished();
     void error(QString err);
};

Использование в отдельном потоке:

// Создание потока
QThread* thread = new QThread;
Worker* worker = new Worker();
// Перемещаем класс-работник в отдельный поток
worker->moveToThread(thread);
// Связываем сигнал об ошибки со слотом обработки ошибок
connect(worker, SIGNAL(error(QString)), this, SLOT(errorHandler(QString)));
// Соединяем сигнал started потока, со слотом process класса-работника, чтобы выполнение началось сразу после запуска потока
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()));
// Запускаем цикл обработки событий для потока, теперь объект готов реагировать на сигналы
thread->start();

От себя добавлю, что такие воркеры отлично справляются с ролью потоков-демонов типа "висеть в памяти и при поступлении сигнала как-то на него реагировать", для этого запуск задачи цепляем не к сигналу started потока, а к тому сигналу, на который надо реагировать и не испускаем сигнал finished при завершении слота с задачей, тогда объект продолжит ждать поступления новых сигналов.

З.Ы. Пример кода старый, коннекты написаны в Qt4-style, лучше переделать на "новый" синтаксис.

READ ALSO
Создание QLabel поверх главного виджета

Создание QLabel поверх главного виджета

Есть основной виджет, который скомпонован с помощью layout-ов и я хочу, чтобы во время выполнения соновной программы при определенных обстоятельствах...

203
Обработка VBI-данных в OSX

Обработка VBI-данных в OSX

Окружение: macOS, драйвер для оцифровки аналогового видео (кроме видео обрабатывается звук и VBI данные)

162
Как загрузить сертификат и ключ при создании QSslSocket?

Как загрузить сертификат и ключ при создании QSslSocket?

Имеется сгенерированный на ubuntu сертификат(servercrt) и ключ(server

178
Получить имя пользователя C++ Linux

Получить имя пользователя C++ Linux

Нужно создать папку в /home/user в Linux, для этого нужно в коде прописать путьКак получить имя этого user-а?

175