Как сделать вторичный поток с постоянный жизненным циклом и управлением

179
21 ноября 2021, 22:00

Имеется объект, который живёт в другом потоке и выполняет различные задачи по запросам или выполняет повторяющиеся задачи.

Более приземленное описание. Через протокол modbus RTU опрашиваются датчики. Датчиков 247 штук. Используются 3 команды, чтение регистров, запись регистров, чтение ID. Управление датчиками может быть ручное или автоматическое. При ручном режиме принимаем данные из главного потока и передаем на датчик, после ответа датчика передаем данные в главный поток для записи в usb устройство и отображение на экране. При автоматическом режиме управляем датчиками по заданному алгоритму и отсылаем ответы датчиков в главный поток для записи на usb устройство и отображения данных.

Вот не знаю как лучше реализовать это.

Answer 1

Можно написать реализацию шаблона проектирования event loop, который будет асинхронно запускать выполнение задач в QThreadPool и ожидать их завершения. В данном примере, взятом из этого репозитория, цикл событий обрабатывает события Await, при поступлении которого изымается Monitor, который, если не завершен, пробрасывается обернутым Await-ом в цикл событий по-новой.

class Engine : public QThread {
  Q_OBJECT
  private:
    QStack<Monitor*>* rethrow;
    QThreadPool* pool;
  public:
    explicit Engine() : QThread(nullptr) {
        qDebug() << "Engine ctor thread: " << QThread::currentThreadId();
        moveToThread(this);
    }
    virtual ~Engine(){
        qDebug() << "Engine dtor";
    }
  protected:
    virtual void run(){
        qDebug() << "Engine thread" <<QThread::currentThreadId();
        pool = new QThreadPool(this);
        rethrow = new QStack<Monitor*>();
        pool -> setMaxThreadCount(QThread::idealThreadCount());
        QEventLoop loop;
        connect(qApp,SIGNAL(aboutToQuit()),&loop,SLOT(quit()));
        qDebug() << "Engine event loop started";
        while(true) {
            if(!loop.processEvents()) {
                if (qApp->closingDown()) {
                    terminate();
                } else {
                    bool continueLoop = rethrow->isEmpty();
                    while(continueLoop) {
                        QCoreApplication::postEvent (
                            this,
                            new Await(rethrow->pop())
                        );
                        continueLoop = rethrow->isEmpty();
                        QThread::msleep(250);
                    }
                }
            }
        }
        qDebug() << "Engine event loop exit";
        pool->deleteLater();
        delete rethrow;
        emit done();
    }
    virtual bool event(QEvent *event){
        if (qApp->closingDown()) {
                return QObject::event(e);
            } else if (e->type() == Event::staticType()) {
                Event* event = static_cast<Event*>(e);
                switch(event->process(this,pool)){
                    case EventResult::AwaiterRethrow:
                        rethrow->push(static_cast<Await*>(e)->getMonitor());
                        return true;
                    default:
                        return true;
                }
            } else {
                return QObject::event(e);
            }
    }
  signals:
    void done();
};
...
class Monitor : public QObject {
  Q_OBJECT
  public:
    explicit Monitor();
    virtual ~Monitor();
    virtual bool isStarted() = 0;
    virtual bool isCanceled() = 0;
    virtual bool isFinished() = 0;
    virtual void start(
        QThreadPool* pool,
        QObject* engine
    ) = 0;
};
...
class Await : public Event {
  private:
    Monitor* monitor;
  public:
    Await(Monitor* monitor);
    virtual ~Await();
    Monitor* getMonitor() const;
    virtual EventResult process(
        QObject *engine,
        QThreadPool *pool
    ) {
        if(monitor->isCanceled() || monitor->isFinished()) {
            qDebug() << "Awaiter monitor cleanup";
            delete monitor;
            return EventResult::Ok;
        } else {
            if(!monitor->isStarted()){
                qDebug() << "Awaiter monitor init";
                monitor->start(pool, engine);
            }
            qDebug() << "Awaiter monitor skip";
            return EventResult::AwaiterRethrow;
        }
    }
};
...
enum EventResult {
    AwaiterRethrow,
    Ok,
};
...
class Event : public QEvent {
  public:
    explicit Event();
    virtual ~Event();
    virtual EventResult process(
        QObject* engine,
        QThreadPool* pool
    ) = 0;
    static QEvent::Type staticType();
};
...
int main(int argc, char *argv[]) {
    QApplication app(argc, argv);
    Engine e;
    e.start();
    QJSValue callback = ...
    Monitors::TimerMonitor* monitor = new Monitors::TimerMonitor(
        callback,
        1000
    );
    QCoreApplication::postEvent(&e, new Events::Await(monitor));
    QObject::connect(&e, SIGNAL(done()), qApp, SLOT(quit()));
    return app.exec();
}

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

READ ALSO
GUI WinAPI Directory

GUI WinAPI Directory

Как называется элемент интерфейса пользователя, который позволяет выбрать путь к файлу или директорию? Такой же элемент, который при установке...

106
Проверка на четность строки типа wchar_t

Проверка на четность строки типа wchar_t

Нужно проверить на четность строку типа wchar_t и добавить в конце символ если она не четнаяНо при работе выдает, что размерность буферного массива...

85
Рекурсивный конструктор

Рекурсивный конструктор

класс TreeNode наследуется от CalcTree, в классе CalcTree создается указатель на TreeNode => вызывается конструктор TreeNode, который рекурсивно вызывает конструктор...

167
Как получить данные о пикселе файла (.bmp) с помощью GetPixel ()?

Как получить данные о пикселе файла (.bmp) с помощью GetPixel ()?

Как с помощью функции GetPixel получить данные о пикселе файла изображения? Или как сделать хендл не к окну консоли, а к файлу?

93