Multithreading in Qt-Widgets

Wenn Sie eine Anwendung mit einer Windows-Oberfläche ausführen, müssen Sie sicherstellen, dass keine Einfrierungen auftreten. Dazu sollten komplexe Berechnungen in einem separaten Thread ausgeführt werden. Das Konzept einer Multithread-Anwendung passt gut zum Qt-Signal-Slots-Ansatz, da keine run () -Methode neu definiert werden muss.

Hauptidee. In einer Multithread-Anwendung werden Berechnungen in einem separaten Thread ausgeführt. Am Ende wird ein Signal ausgegeben, das das Ergebnis in seinen Argumenten überträgt. Ein Slot, der bereits im Besitz von MainWindow ist, wird aufgerufen. Die Berechnungsergebnisse werden in den Argumenten des Slots enthalten sein und es wird nicht schwierig sein, sie auszugeben.

In Analogie zu Mikrocontrollern ist ein Signal ein Übergang entlang eines Interruptvektors, und ein Slot ist selbst ein Interrupt-Handler. Damit ein "Interrupt" auftritt, muss das Signal gesendet werden: emit mysignalvoid (); dann beginnt Qt mit einem „Handler“ (Slot) danach zu suchen. In Qt können Sie mehrere Slots an ein einzelnes Signal hängen.

Die folgende Abbildung zeigt den Algorithmus der Demoanwendung. Der Thread, der jede Sekunde verarbeitet, fragt der Legende nach ein USB-HID-Gerät ab. Dann wird ein Sendesignal ausgegeben, das einen Slot verarbeitet, der bereits zu MainWindow gehört und der dementsprechend die Möglichkeit hat, auf dem Formular zu zeichnen.



Wir haben also Klassen, die den Signal-Slot-Ansatz verwenden können. Zu diesem Zweck verwendet die Deklaration das Makro 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; 

Um das Ergebnis von Berechnungen in den Argumenten zu übergeben, müssen die für die Argumente verwendeten Typen registriert werden. Sie können dies an verschiedenen Stellen tun, normalerweise jedoch im Konstruktor der Klasse, der dann mit den folgenden Typen funktioniert:

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


Als Nächstes wird im Konstruktor des Verarbeitungsthreads ein Zeitgeber erstellt. Jede Sekunde wird der Verarbeitungssteckplatz updateUSBDataCallback aufgerufen. Achten Sie auf die Syntax der Signal-Slot-Verbindung in Qt5.

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

Das Folgende ist der Körper des Verarbeitungs-Thread-Slots. Der ganze langgedachte Code sollte hier sein.

 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; } } 

Um dies zu demonstrieren, wird der Inhalt des Arrays unmittelbar nach dem Senden des Signals an den MainWindow-Slot dreist geändert. Und da das Array als Zeiger übergeben wird, wird ein Dirty Read erhalten. Um dies zu verhindern, muss das Signal vom Verarbeitungsthread auf eine bestimmte Weise mit dem GuiUpdateCallback () -Steckplatz verbunden werden:

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

In diesem Fall wird der Verarbeitungsthread durch Aussenden eines Signals bis zum Ende des GuiUpdateCallback () -Slots blockiert.

Wenn Sie das lange "uint8_t const *" im Programmtext verwirrt, können Sie das Synonym TU8PTR erhalten:

 typedef uint8_t const * TU8PTR; 

Quellcode

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


All Articles