Бесконечный цикл в QThread

Итак, программа работает с джойстиком, данные с которого выводятся в интерфейс и передаются по Modbus. Для этого получение и обработку данных с джойстика было решено закинуть в отдельный поток. Был оформлен отдельный класс в joyhid.h:

#ifndef JOYHID_H
#define JOYHID_H

#include <QObject>
#include <cstdint>
#include "hidapi.h"

typedef struct _HID_JOYSTK_Info
{
    uint16_t              X;
    uint16_t              Y;
    uint16_t              Z;
    uint16_t              X_low;
    uint16_t              Y_low;
    uint8_t               buttons[5];
}
HID_JOYSTK_Info_TypeDef;

class JoyHID : public QObject
{
    Q_OBJECT

public:
    explicit JoyHID(QObject *parent = nullptr);

public slots:
    void joySlot();

signals:
    void joySignal(HID_JOYSTK_Info_TypeDef *obj);

private:
    void joyHIDUpdate(HID_JOYSTK_Info_TypeDef *obj);
};

#endif // JOYHID_H

В mainwindow.cpp закидывается в отдельный поток:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QThread *joy_thread = new QThread;
    JoyHID *joystck = new JoyHID;
    joystck->moveToThread(joy_thread);
    connect(joy_thread, SIGNAL(started()), joystck, SLOT(joySlot()));
    connect(joystck, &JoyHID::joySignal, this, &MainWindow::uiSlot);
    joy_thread->start();
}

В joyhid.cpp слот joySlot() выполняет вот такой незамысловатый код:

void JoyHID::joySlot()
{
    joyHIDUpdate(&joyInfo);
    emit joySignal(&joyInfo);
}

Отрабатывается одна итерация и я получаю единоразовое обновление значений. Но мне необходимо, чтобы поток крутился постоянно, обновляя значения с джоя. Первой идеей было засунуть в слот joySlot(), например, while(true), но идея потерпела крах. Приложение висло наглухо. Собственно, вопрос, какие работоспособные альтернативы моего решения существуют и что лучше применить в данном случае?


Ответы (2 шт):

Автор решения: Sergey Tatarincev

Бесконечный цикл while(true) блокирует эвентлуп потока и события/сигналы перестают обрабатываться. Самый простой способ - инициировать обработку событий в вашем цикле:

void JoyHID::joySlot()
{
    while(true){
        joyHIDUpdate(&joyInfo);
        emit joySignal(&joyInfo);
        qApp->processEvents();
    };
}

или излучить в конце слота повторно сигнал (он встанет в очередь обработки и обработается после всего остального)

joihid.h

class JoyHID : public QObject
{
//.....
signals:
    void joySignal(HID_JOYSTK_Info_TypeDef *obj);
    void joyWork();
//.....
}

joihid.cpp

JoyHID::JoyHID(QObject *parent)
{
    //.....
    connect(this, &JoyHID::joyWork, this, &JoyHID::joySlot);
}

void JoyHID::joySlot()
{
    joyHIDUpdate(&joyInfo);
    emit joySignal(&joyInfo);
    emit joyWork();
}

А еще лучше ваш класс представить в виде машины с конечными состояниями (см QStateMachine). Это будет наиболее красиво и правильно

→ Ссылка
Автор решения: Bearded Beaver

Как альтернативный вариант могу предложить использовать таймер, чтобы сигнал с состоянием джойстика испускался раз в заданный интервал времени. В конструкторе вашего JoyHID примерно так:

timer = new QTimer(this);
timer->setInterval(10); // will emit 100 times per second
connect(timer, &QTimer::timeout, this, &JoyHID::joySlot);
timer->start();

Кстати при таком подходе если метод joySlot нужен только для обслуживания испускания сигнала, его можно заменить на лямбду:

connect(timer, &QTimer::timeout, this, [this]() {
    joyHIDUpdate(&joyInfo);
    emit joySignal(&joyInfo);
});
→ Ссылка