Как стать автором
Обновить

Многопоточность в Qt Widgets

Время на прочтение3 мин
Количество просмотров9.8K
При работе приложения с оконным интерфейсом важно обеспечить отсутствие зависаний. Для этого сложные вычисления стоит запускать в отдельной нити. Концепция многопоточного приложения отлично сочетается с подходом сигналы-слоты Qt, при этом совершенно не нужно переопределять никакой метод run().

Основная идея. В многопоточном приложении вычисления проводятся в отдельной ните, по окончанию излучается сигнал, передающий результат в своих аргументах. Слот, принадлежащий уже MainWindow, будет вызван. Результаты вычислений окажутся в аргументах слота и не составит труда вывести их.

Проводя аналогию с микроконтроллерами, сигнал — это переход по вектору прерывания, а слот — сам обработчик прерывания. Чтобы возникло «прерывание», сигнал необходимо излучить: emit mysignalvoid(); тогда Qt начнёт искать ему «обработчик» (слот). В Qt можно на один сигнал повесить много слотов.

На следующем рисунке представлен алгоритм работы демонстрационного приложения. Нить обработки ежесекундно опрашивает по легенде USB HID устройство. Затем излучается (emit) сигнал, который обрабатывает слот, принадлежащий уже MainWindow и имеющий соответственно возможность рисовать на форме.



Итак, имеем классы, способные использовать подход сигналы-слоты. Для этого в их объявлении используется макрос Q_OBJECT.

class Worker : public QObject
{
    Q_OBJECT  //теперь можем использовать сигналы-слоты в классе

public:
    QTimer *timerDeviceRead;  //будем каждую секунду вызывать собственный слот GuiUpdateCallback
    Worker();

public slots:
    void updateElectropipData();

signals:
    void GuiUpdatePlease(uint8_t const *arrptr,size_t);
};

class MainWindow : public QMainWindow  //класс окна с GUI
{
    Q_OBJECT  
public slots:
    void GuiUpdateCallback(uint8_t const *arrptr, size_t);
private:
    Ui::MainWindow *ui;  //доступ к кнопкам и иже с ними
    QThread *thread;

Чтобы передавать результат вычислений в аргументах необходимо зарегистрировать типы, используемые для аргументов. Сделать это можно в разных местах, но обычно — в конструкторе класса, который потом будет с этими типами работать:

Worker::Worker(){
    qRegisterMetaType<std::size_t>("size_t");  
    qRegisterMetaType<uint8_t const *>("uint8_t const *");


Обратите внимание, что в Qt уже существует множество своих типов, их не нужно регистрировать. Например, имеет смысл использовать quint8 вместо uint8_t.

Далее в конструкторе нити обработки создаётся таймер. Каждую секунду будет вызываться слот обработки updateUSBDataCallback. Обратите внимания на синтаксис подключения сигнал-слот в Qt5.

    this->timerDeviceRead = new QTimer();
    connect(Worker::timerDeviceRead, &QTimer::timeout, this, &Worker::updateUSBDataCallback);
    this->timerDeviceRead->start();

Ниже приведено тело слота нити обработки. Здесь должен находится весь долгодумающий код.

void Worker::updateUSBDataCallback(){
    size_t mysize = 65;
    uint8_t buf[65] = { "I like USB HID" };
    emit GuiUpdatePlease(buf,mysize);  //излучение сигнала к слоту MainWindow
	//Подмена:
    for(int i =0;i<64;i++){
        buf[i]=i+'0';
        if (i+'0' > 250) i=0;
    }
}

Для демонстрации здесь сразу после излучения сигнала к слоту MainWindow, когда вероятно, что слот уже начал, но ещё не успел считать данные целиком, нагло подменяется содержимое массива. А так как массив передаётся по указателю, получается грязное чтение. Для предотвращения подобной ситуации сигнал из нити обработки должен быть связан со слотом GuiUpdateCallback() определённым образом:

MainWindow::MainWindow{
connect(worker, &Worker::GuiUpdatePlease, this, &MainWindow::GuiUpdateCallback, Qt::BlockingQueuedConnection);

В таком случае, излучив сигнал, нить обработки блокируется до окончания работы слота GuiUpdateCallback().

Если в тексте программы вас смущает длинное «uint8_t const *», то можно завести синоним TU8PTR:

typedef uint8_t const * TU8PTR;


Другие материалы по теме: QThread — работа с потоками с помощью moveToThread
Почему не получается правильно завершить поток?

Исходный код
Теги:
Хабы:
Всего голосов 3: ↑1 и ↓2+1
Комментарии11

Публикации

Истории

Работа

QT разработчик
3 вакансии
Программист C++
104 вакансии

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань