Seite 1 von 1

(Re)-Init Funktion mit QThread-Liste

Verfasst: 9. Juli 2015 14:15
von leif
Hallo,

habe da einen Schnipsel der funktioniert wie er soll, aber ich befürchte, dass es ziemlich unsauber ist.
Eine Klasse dient zur Steuerung und Überwachung mehrerer Worker-Objekte. Das läuft über Listen, d.h die Worker-Objekte werden in einer Liste abgelegt. Analog werden QThreads für jedes Worker-Objekt erzeugt, wo das Worker-Objekt reingesteckt wird, ebenfalls in einer Liste gesammelt. Das übernimmt eine Init-Funktion, die ich gerne auch zum Reinit einsetzen wollte, d.h. die sollte mehrmals nacheinander aufgerufen werden können.

Code: Alles auswählen

QList<QThread *> _workerThreads;
QList<Worker *> _workers;
Die zwei Listen sind die einzige Referenz zu den QThreads und den aufgenommenen Worker-Objekten.

Code: Alles auswählen

void Operator::init()
{
  // ist clear gleich clean? ...wohl kaum.
  _workerThreads.clear();
  _workers.clear();

  int numberOfPorts = QSerialPortInfo::availablePorts().length();

  for(int i = 0; i < numberOfPorts; i++)
  {
    _workerThreads.append(new QThread);
    _workers.append(new Worker);
    _workers.last()->moveToThread(_workerThreads.last());

    connect(_workerThreads.last(), SIGNAL(started()), _workers.last(), SLOT(init()));
  }

  for(int i = 0; i < workerThreads.length(); i++)
  {
    _workerThreads.at(i)->start();
  }
}
Ich denke der Verlauf ist soweit klar und auch worin ich das Problem sehe. Die clear()-Funktion von QList wird sicherlich nicht die Worker-Objekte und QThreads löschen. Es funktioniert zwar soweit, aber ich brauche wohl unbedingt einen Rat, denn wie eine passable Lösung sieht mir das nicht aus. Falls noch Fehler im Code drin sind: den habe ich gerade nur so runtergetippt, damit wir hier ein Bild haben.
Es darf davon ausgegangen werden, dass bei allen Worker-Objekten ein Bereitschafts-Status vorliegt, d.h. in den QThreads nichts mehr läuft, sowie die Zeiger aus den Listen entfernt werden.
Die Init-Funktion eines Worker-Objekts wird aufgerufen sobald der zugehörige QThread gestartet wurde. Da passiert nicht mehr viel.

Code: Alles auswählen

void Worker::init()
{
  _port = new QSerialPort;
}
So lässt sich dann in der Worker-Klasse auch mit dem _port-Member reibungslos arbeiten. Bereitschaft eines Workers heißt auch, dass der Port geschlossen ist.
Ich habe noch nicht einmal geschaut, ob vielleicht der Destruktor der Worker-Klasse beim Entfernen des Zeigers aus der Liste aufgerufen wird.

Vielen Dank.
Grüße,
leif

Re: (Re)-Init Funktion mit QThread-Liste

Verfasst: 9. Juli 2015 15:33
von leif
Hallo nochmal!

Also mein Ansatz wäre vor dem clear() der beiden Listen einerseits allen QThreads ein deleteLater() zu geben und die Worker-Objekte zuvor direkt mit einem delete zu entfernen. Die Frage wäre, ob ich damit wirklich fein raus bin. Damit wäre also nicht mehr nur die Frage, wie man es macht, sondern ob es so sauber wäre:

Code: Alles auswählen

void Operator::init()
{
  for(int i = 0; i < _workerThreads.length(); i++)
  {
    delete _workers.at(i);
    _workerThreads.at(i)->quit();
  }

  _workerThreads.clear();
  _workers.clear();

  int numberOfPorts = QSerialPortInfo::availablePorts().length();

  for(int i = 0; i < numberOfPorts; i++)
  {
    _workerThreads.append(new QThread);
    _workers.append(new Worker);
    _workers.last()->moveToThread(_workerThreads.last());

    connect(_workerThreads.last(), SIGNAL(started()), _workers.last(), SLOT(init()));
    connect(_workerThreads.last(), SIGNAL(finished()), _workers.last(), SLOT(deleteLater()));
  }

  for(int i = 0; i < workerThreads.length(); i++)
  {
    _workerThreads.at(i)->start();
  }
}
Schließlich heißt es jetzt, "QThread: destroyed while thread is still running".

Edit: Die Meldung ist weg, nachdem ich den Code nochmals angepasst habe, indem ich mein direktes deleteLater() an den Thread durch ein quit() ersetzt und eine Verbindung des finished()-Signals mit dem deleteLater()-Slot des Threads angelegt habe. Das ist mit diesem Edit im Code-Block dieses Beitrags bereits enthalten.

Re: (Re)-Init Funktion mit QThread-Liste

Verfasst: 23. Juli 2015 12:42
von RHBaum
generell:
1. deleteLater ruft den destruktor auf, indem es eine Message in die queue stellt, die genau das tut (und bissi mehr).
Das garantiert nur, das alle alle Messages in der Queue, und damit alle Signals und Slot Verbindungen abgearbetet wurden, die vor dem delete later in die queue gestellt wurden. mehr nicht.

Es garantiert nicht, das der Thread an sich, sich beendet hat, bevor das kapselnde Object (QThread) zerstört wird.
Schau was ein join macht ....
Anmerkung: nen join in nem Destruktor ist meist auch keine gute idee :-) Threads wollen gestoppt werden und verdienen nen auch nen stopped/ finished status! Bau das in deine Verwaltung ein :-) Threadprogrammierung ist eben nicht trivial ... und eigentlich braucht jeder thread nen kleinen zustandsautomaten ....

2. deine Begriffsverwendung ist irritierend :-)
Worker sind eigentlich abstraktere form von threads .... in der reinen thread programmierung ist nen Worker == Thread. Warum da nen unnerschied machen?
Wenn jeder deiner worker einen thread hat und umgekehrt, und die lifecycles zusammenhängen, stimmern die Begriffe wieder :-)
Aber dann macht es keinen Sinn 2 Listen zu führen, für Worker und Für threads :-)

Ich vermute du willst sowas wie einen Threadpool ... und die Objecte die für die Threads die Aufgaben darstellen sind dann eigentlich Jobs !

Ciao ..