Multithreading en Qt Widgets

Al ejecutar una aplicación con una interfaz en ventana, es importante asegurarse de que no haya bloqueos. Para esto, los cálculos complejos deben ejecutarse en un hilo separado. El concepto de una aplicación multiproceso va bien con el enfoque de ranuras de señal Qt, sin necesidad de redefinir ningún método run ().

Idea principal. En una aplicación multiproceso, los cálculos se llevan a cabo en un hilo separado, al final se emite una señal que transmite el resultado en sus argumentos. Se abrirá una ranura que ya es propiedad de MainWindow. Los resultados del cálculo estarán en los argumentos de la ranura y no será difícil generarlos.

Dibujando una analogía con los microcontroladores, una señal es una transición a lo largo de un vector de interrupción, y una ranura es un controlador de interrupción en sí. Para que ocurra una "interrupción", se debe emitir la señal: emit mysignalvoid (); entonces Qt comenzará a buscarlo con un "controlador" (ranura). En Qt, puede colgar muchas ranuras en una sola señal.

La siguiente figura muestra el algoritmo de la aplicación de demostración. El hilo de procesamiento cada segundo interroga, según la leyenda, un dispositivo USB HID. Luego se emite una señal de emisión, que procesa una ranura que ya pertenece a MainWindow y que, en consecuencia, tiene la capacidad de dibujar en el formulario.



Entonces, tenemos clases que pueden usar el enfoque de ranura de señal. Para hacer esto, su declaración utiliza la 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 pasar el resultado de los cálculos en los argumentos, es necesario registrar los tipos utilizados para los argumentos. Puede hacerlo en diferentes lugares, pero generalmente en el constructor de la clase, que luego funcionará con estos tipos:

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


A continuación, se crea un temporizador en el constructor del subproceso de procesamiento. Cada segundo, se llamará a la ranura de procesamiento updateUSBDataCallback. Preste atención a la sintaxis de la conexión de la ranura de señal en Qt5.

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

El siguiente es el cuerpo de la ranura del subproceso de procesamiento. Todo el código de pensamiento largo debería estar aquí.

 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 demostrar aquí, inmediatamente después de que la señal se emite a la ranura MainWindow, los contenidos de la matriz se modifican descaradamente. Y dado que el puntero pasa la matriz, se obtiene una lectura sucia. Para evitar esta situación, la señal del hilo de procesamiento debe estar conectada a la ranura GuiUpdateCallback () de cierta manera:

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

En este caso, al emitir una señal, el subproceso de procesamiento se bloquea hasta el final de la ranura GuiUpdateCallback ().

Si el largo "uint8_t const *" lo confunde en el texto del programa, puede obtener el sinónimo TU8PTR:

 typedef uint8_t const * TU8PTR; 

Código fuente

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


All Articles