Multithreading em Qt Widgets

Ao executar um aplicativo com uma interface com janelas, é importante garantir que não haja congelamentos. Para isso, cálculos complexos devem ser executados em um encadeamento separado. O conceito de um aplicativo multiencadeado vai bem com a abordagem de slots de sinal Qt, sem a necessidade de redefinir qualquer método run ().

Ideia principal. Em uma aplicação multithread, os cálculos são realizados em uma thread separada, ao final é emitido um sinal, transmitindo o resultado em seus argumentos. Um slot já pertencente ao MainWindow será chamado. Os resultados do cálculo estarão nos argumentos do slot e não será difícil produzi-los.

Fazendo uma analogia com microcontroladores, um sinal é uma transição ao longo de um vetor de interrupção e um slot é um manipulador de interrupção. Para que uma “interrupção” ocorra, o sinal deve ser emitido: emitir mysignalvoid (); então o Qt começará a procurá-lo com um "manipulador" (slot). No Qt, você pode pendurar muitos slots em um único sinal.

A figura a seguir mostra o algoritmo do aplicativo demo. O segmento de processamento a cada segundo interroga de acordo com a legenda de um dispositivo USB HID. Em seguida, é emitido um sinal de emissão, que processa um slot que já pertence à MainWindow e que, consequentemente, tem a capacidade de desenhar no formulário.



Portanto, temos classes que podem usar a abordagem de slot de sinal. Para fazer isso, a declaração deles usa a macro 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; 

Para passar o resultado dos cálculos nos argumentos, é necessário registrar os tipos usados ​​para os argumentos. Você pode fazer isso em locais diferentes, mas geralmente no construtor da classe, que funcionará com estes tipos:

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


Em seguida, um cronômetro é criado no construtor do encadeamento de processamento. A cada segundo, o slot de processamento updateUSBDataCallback será chamado. Preste atenção à sintaxe da conexão do slot de sinal no Qt5.

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

A seguir, está o corpo do slot de thread de processamento. Todo o código de pensamento longo deve estar aqui.

 void Worker::updateUSBDataCallback(){ size_t mysize = 65; uint8_t buf[65] = { "I like USB HID" }; emit GuiUpdatePlease(buf,mysize); //  //: for(int i =0;i<64;i++){ buf[i]=i+'0'; if (i+'0' > 250) i=0; } } 

Para demonstrar aqui, imediatamente após o sinal ser emitido para o slot MainWindow, o conteúdo da matriz é modificado descaradamente. E como a matriz é passada pelo ponteiro, é obtida uma leitura suja. Para evitar essa situação, o sinal do encadeamento de processamento deve ser conectado ao slot GuiUpdateCallback () de uma certa maneira:

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

Nesse caso, emitindo um sinal, o encadeamento de processamento fica bloqueado até o final do slot GuiUpdateCallback ().

Se o longo "uint8_t const *" confunde você no texto do programa, você pode obter o sinônimo TU8PTR:

 typedef uint8_t const * TU8PTR; 

Código fonte

Source: https://habr.com/ru/post/pt485324/


All Articles