Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Klassen Funktion mit Parameter und Rückgabewert in ein Thread ausführen.



  • Hallo,

    ich habe eine Anwendung geschrieben die Daten von der Festplatte einliest und in einer TableWidgetView Anzeigt. Die GUI und die Filesystem Operationen habe ich in Klassen getrennt. Es kann jedoch sein das dass Verzeichnis das eingelesen werden soll recht groß ist, dann hängt die GUI. Deswegen habe ich mir einige Thread Beispiele angesehen und kleine Tutorials ausprobiert. Aber ich bin noch nicht dahinter gekommen wie man mit dem Thread interagiert und richtig anwendet.

    Hier im Forum hat DeiVadder ein Example geschrieben und im Github veröffentlicht: https://gist.github.com/DeiVadder/1b9f5505920f52c33453a9ebe60e33e0

    Dies war vor drei Jahren, ich denke, an die Thematik wird sich nichts verändert haben und es sollte noch so gehen oder?

    //!QtConcurrent
        connect(ui->btnQtConcurrent, &QPushButton::clicked, this,&MainWindow::startConcurrent);
        connect(this, &MainWindow::newConcurrentValue, this, &MainWindow::updateVonConcurrent);
    

    Ich gehe mal davon aus, das ich dies nicht brauche ist nur ein Beispiel wie es mit QtConcurrent gehen würde oder?
    QtConcurrent kann ich nicht verwenden, denn es scheint nicht mit anderen Klassen zu Arbeiten. (Non-Static Function)

    Deswegen habe ich mich auf dieses Stück Konzentriert.

     //!QThread
        //Initalisierung des Threads und der Workerklasse
        QThread * workerThread = new QThread();
        Worker *workerObject = new Worker();
        workerObject->moveToThread(workerThread);
        connect(workerThread, &QThread::started, workerObject, &Worker::init);
    
        //QThread und Worker aufräumen bei Programmende
        connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
        connect(workerThread, &QThread::finished, workerObject, &Worker::deleteLater);
        connect(qApp, &QApplication::aboutToQuit, workerThread, &QThread::quit);
    

    Was ich noch nicht verstanden habe, wie kann ich eine Funktion starten die ein Parameter erwartet?
    Wie stelle ich fest ob der Thread fertig ist (wenn Funktion erledigt ist) und Arbeite mit das Ergebnis (Rückgabewert von Funktion) weiter ohne das dies die GUI Blockiert.

    Im Grunde hat sich für mich QtConcurrent und QFuture<Object> richtig angehört, aber konnte wie schon oben erwähnt die Funktion einer Klasse nicht ausführen. Ich möchte irgendwann auch ein Button einbauen, der den Thread (also die gestartete Funktion) wieder beendet falls es den Benutzer doch zu lange dauert.

    Geht das mit beiden? Also QThread und QFuture/QtConcurrent?
    Was nutzt man Wann und was würdet Ihr mir empfehlen zu verwenden und warum?

    Fall jemand ein Example kennt das ein Thread Startet mit Parameter und mit dem Rückgabewert weiter gearbeitet wird, wäre ich sehr dankbar. Bisher konnte ich noch nichts passendes finden und alle meine Versuche sind bisher gescheitert.

    Gruß
    Sandy


  • Moderators

    Hi @knasan

    Wow, ich hatte vergessen, dass das Repository existiert 🙈

    Ich würde aber eher empfehlen diese hier als Vorlage zu nehmen:
    https://github.com/DeiVadder/QtThreadExample

    Ist neuer und umfasst alle (Qt) threading Methoden.
    Wenn ich die Zeit finde gibts auch nochmal nen Video dazu auf YT 🤞

    Zu deinen Fragen:

    Was ich noch nicht verstanden habe, wie kann ich eine Funktion starten die ein Parameter erwartet?

    Ganz einfach, definiere dein Funktion als slot. Dann definiere ein Signal das ebenso wie deine Funktion eine variable als Argument hat, dann kannst du ganz einfach über SIGNAL&SLOT und connect die beiden miteinander verbinden.

    Vom main thread aus dann einfach das Signal ausführen (emit mySigna(myArgument);)

    Der Weg zurück funktioniert genauso, nur das diesmal dein worker ein Signal emitiert und deine Gui klasse einen Slot hat der aufgerufen wird.

    Geht das mit beiden? Also QThread und QFuture/QtConcurrent?

    hup geht mit beiden, btw QFuture ist die einzige Möglichkeit für nen Rückgabewert, aber Signal&Slot geht mit beiden.

    Grüße



  • Hi @J-Hilk

    Danke für den Link!
    Das ist vermutlich etwas zu viel für mich alles so gemischt zu haben, aber ich habe es mal versucht.
    Der Thread startet, er führt auch meine gewünschte Funktion auf, lediglich ein Zeichen das er fertig ist geht bei mir noch nicht.

    in MainWindow.h

    ....
    void startGetFiles(); // wird mit dem Thread verbunden. Diese Funktion ruft ein langer Prozess auf um Dateien einzulesen.
    
    signals:
      void finishedGetFiles(QStringList collectDataFiles);
    private slots:
      void onFinishedGetFiles(QStringList collectDataFiles);
    

    MainWindow.cpp

    void MainWindow::startGetFiles() {
      qDebug() << "StartGetFiles from thread";
      QString dirpath = ui->lineEdit_Destination->text();
      collectDataFiles = fsu.getFiles(dirpath); // Hier die Lange funktion, als Rückgabewert QStringList
      emit finishedGetFiles(collectDataFiles); // hier soll (denke ich) den Thread gesagt werden ich möchte diese QStringList um diese weiter verwenden zu können.
    }
    

    onFinishedGetFiles, ähnlich wie onOperationDone, wie ist dies angebunden ... mit autoconnect?

    void MainWindow::onFinishedGetFiles(QStringList collectDataFiles) {
      qDebug() << "onFinishedGetFiles";
      // Enable pushButton collect and doit
      ui->pushButton_Collect->setDisabled(false);
      ui->pushButton_DoIt->setDisabled(false);
    
      // Fill tableFilesToRename
      int row = 0, col = 0;
      for (int i = 0; i < collectDataFiles.size(); i++) {
        insertTableFilesToRename(row, col, collectDataFiles[i]);
        row++;
      }
    .....
    

    Wenn der Button geklickt wird, starte ich den Thread so.

    ...
     QThread *thread = QThread::create([=]() -> void { startGetFiles(); });
        connect(this, &MainWindow::finishedGetFiles, thread, &QThread::quit);
        connect(thread, &QThread::finished, thread, &QThread::deleteLater);
    ...
    

    Mir fehlt also ein Signal das besagt, die Operation die ich möchte ist fertig.

    1. Wie setzte ich so ein Signal?
    2. ist onOperationDone ein autoconnect für operationDone? oder Wie wird diese Funktion aufgerufen?

    Vielen Dank für die Hilfe.

    Ich wünsche ein schönes Wochenende!

    Gruß
    Sandy



  • Jetzt hat es bei mir klick gemacht und es scheint zu funktionieren.
    Ich musste ein Signal und ein Slot definieren mit den gleichen Parameter.
    In der Funktion das ich als Thread starte habe ich ein

    emit finishedGetFiles(collectDataFiles); // MeinSignal(MeinObject);
    

    Und wo der Thread initialisiert wird, habe ich ein connect mit den Signal und dem Slot hergestellt.

    connect(this, &MainWindow::finishedGetFiles, this,
                &MainWindow::onFinishedGetFiles);
    

    Und die Tabelle wird auch gefüllt wie gewünscht.

    Ist das Prinzip richtig oder habe ich es richtig verwendet?

    Danke für eure Hilfe.

    Gruß
    Sandy


  • Moderators

    @knasan
    Oh, du hast dich für Option 4 entschieden, das ist die, die ich am wenigsten empfehlen würde 🙈

    Nummer 1 , dann 2 , 3 und dann irgendwann 4. QThread::create ist nicht gemacht um mit Klassenfunktionen zu arbeiteten.

    Hab es nur der Vollständigkeit halber im Beispiel integriert!

    Von dem was ich so lese scheint es ja zu klappen ohne es genauer, im Ganzen, zu sehen ist es schwer ein Urteil abzugeben 😉



  • Danke für den Hinweis.
    Ich werde mal deine Empfehlung durchgehen.
    Was ich Interessiert, warum du genau diese Reihenfolge empfiehlst, was ist die Vor- und Nachteile usw.

    Danke

    Von dem was ich so lese scheint es ja zu klappen ohne es genauer, im Ganzen, zu sehen ist es schwer ein Urteil abzugeben

    Ich Arbeite gerade an einem OpenSource Projekt daher kein Problem den Quellcode zu veröffentlichen. Ich kann den Code ohne Thread veröffentlichen, diese Funktioniert soweit ja. Diesen kann man sicherlich an einige stellen noch verbessern und Optimieren. Ist ein Projekt um C++/Qt zu Lernen und gleichzeitig ein vernünftiges eigenständiges Tool zu entwickeln.


  • Moderators

    @knasan said in Klassen Funktion mit Parameter und Rückgabewert in ein Thread ausführen.:

    Was ich Interessiert, warum du genau diese Reihenfolge empfiehlst, was ist die Vor- und Nachteile usw.

    Der vorgesehene Weg für ein multithreaded Programm ist der Weg über QThread.

    Es gibt im Prinzip 2 Möglichkeiten wie man mit QThread arbeitet. Die beste, und empfohlene, version ist der Worker-Object.

    Hat den Vorteil du packst deine lange Operation/Funktionen einfach in ihre eigene Klasse, und kommunizierst mit ihr über Signal und Slots. Wenn alles so funktioniert wie du es willst, gehst du hin und schiebst diese Klasse einfach über moveToThread in ihren eigenen QThread und du bist fertig.

    Kein tieferes Wissen über Qt oder multithreading, mutex, shared data etc nötig.

    Version 2 ist QThread subclass. Funktioniert ähnlich wie der Worker, nur das deine Basis Klasse QThread anstatt QObject ist.

    Nachteil, es ist super einfach was falsch zu machen, Objekte müssen in run erstellt werden, sonst leben sie im falschen Thread. Man darf nicht vergessen exec() aufzurufen, oder Signal&Slot funktionieren nicht richtig etc. pp

    QtConcurrent ist nochmal eine ebene Höher als QThread, es managed automatisch die threads in einem Threadpool. D.h die Anzahl der Threads passt sich der Anzahl der Kerne automatisch an, und du must das nicht per hand machen.
    Es ist mehr dazu gedacht das du es auf container anwendest, für filter, maping etc.
    Es ist sein eigenes Modul, und du musst es explicit in der *.pro file verlinken

    QThread:.create ist im Prinzip ein Qt-wrapper für std::async
    Es ist nicht dafür gedacht Klassen Funktionen aufzurufen, der einzige Grund, warum es bei dir und in meinem Beispiel funktioniert, ist die Nutzung des Lambdas.
    Und das ist eigentlich gecheated ;)


Log in to reply