[geloest] QTimer-Objekt in Thread

Alles rund um die Programmierung mit Qt
Antworten
ColonelMoW
Beiträge: 30
Registriert: 15. April 2008 16:24

[geloest] QTimer-Objekt in Thread

Beitrag von ColonelMoW »

Guten Abend,

Ich habe eine Server geschrieben (abgeleitet von QTcpServer), der die Client-Anfragen in Threads verarbeitet. Hier mal die ueberschriebene incommingConnection-Funktion:

Code: Alles auswählen

void receiver::incomingConnection(int socketDescriptor)
{
   CommunicatorCall *comm = new CommunicatorCall(...); //kein Parent!
   connect( comm, SIGNAL(finished()),
                comm, SLOT(deleteLater()), Qt::DirectConnection);

   //die error- und event-Messages werden einfach weitergereicht:
   connect( comm, SIGNAL(errorMessage(const QString&)),
                           SIGNAL(errorMessage(const QString&)), Qt::QueuedConnection);
   connect( comm, SIGNAL(eventMessage(const QString&)),
                           SIGNAL(eventMessage(const QString&)), Qt::QueuedConnection);

   comm->start();
}
Die CommunicatorCall-Klasse ist von QThread abgeleitet. Sie hat einen Zeiger auf ein QTimer-Objekt in ihrem private-Part:

Code: Alles auswählen

...
private:
   QTimer *timeout;
...
In der ueberschriebenen run()-Routine von CommunicatorCall initialisiere ich den Timer wie folgt:

Code: Alles auswählen

   timeout = new QTimer;
   timeout->setSingleShot(true);
   timeout->setInterval(10 * 1000);    //our timeout is 10 sec 
...
   connect( timeout, SIGNAL(timeout()),
                this, SLOT(timeoutOccurred()), Qt::DirectConnection);
...
   timeout->start();
der Slot timeoutOccurred() macht nichts anderes, als dass er versucht die Verbindung zum client zu trennen und mittels Timer erneut 10 Sekunden auf das disconnected()-Signal vom Socket wartet. Kommt dieses rechtzeitig vor Timer-Ablauf, so wird der disconnected()-Slot automatisch aufgerufen, ansonsten tue ich das manuel:

timeoutOccurred(void):

Code: Alles auswählen

   timeout->stop();

   socket->disconnectFromHost();
   
   disconnect( timeout, SIGNAL(timeout()),
               this, SLOT(timeoutOccurred()));
   
   connect( timeout, SIGNAL(timeout()),
            this, SLOT(disconnected()), Qt::DirectConnection);
   timeout->start();
Im disconnected-Slot halte ich den Timer an, und rufe den Quit-slot auf, so dass sich der Thread selbst aufloest:

Code: Alles auswählen

   timeout->stop();
   quit();  //exit thread
Aus irgendeinem Grund erhalte ich zwischen dem quit()-Aufruf und dem Destruktur-Aufruf des Thread-Objektes die Meldung:
QObject::startTimer: QTimer can only be used with threads started with QThread

Allerdings taucht sie nur ab und zu auf und ist damit leider nicht reproduzierbar. Hat jemand eine Idee, an was das liegen koennte bzw. wie man an die Sache rangehen kann?
Ich bin fuer jeden Tipp dankbar!

Col
Zuletzt geändert von ColonelMoW am 13. Mai 2008 20:22, insgesamt 1-mal geändert.
ColonelMoW
Beiträge: 30
Registriert: 15. April 2008 16:24

Beitrag von ColonelMoW »

okay - ich glaube den Fehler gefunden zu haben:
Es war das disconnected() signal des Sockets mit meinem disconnected-Slot im Thread ueber eine Qt::DirectConnection verbunden. Der Slot wird damit aufgerufen, sobald das Signal kommt, und das kann zu einem unguenstigen Zeitpunkt sein:

Der Slot wurde also sofort aufgerufen, sobald der socket die Verbindung abgebaut hatte. Es wurde also alles angehalten und direkt in den disconnected-Slot gesprungen. Dieser hat nun den quit()-Slot des Threads aufgerufen. Damit war der Thread beendet, obwohl ich noch manchmal im Slot von oben steckte, in dem ich dem Socket den Verbindungsabau beauftragt hatte.
Die Meldung kam nun zustande, da ich nach dem disconnectFromHost() des Sockets, noch den Timer startete. Dieser Timer gehoert aber ja dem Thread - der zu diesem Zeitpunkt nicht mehr existierte.
Also:

1. socket -> Verbindung abbauen (in Slot A)
2. Signal disconnected kommt durch, auch wenn man mit Slot A noch nicht fertig war - die Bearbeitung wird einfach unterbrochen.
3. disconnected hat den Thread beendet.
4. man ist wieder zu Slot A zurueckgekehrt, da das ja noch nicht fertig war
5. man hat noch am Timer herumgeschraubt, obwohl der Thread dem der Timer gehoerte nicht mehr existierte

Resultat:
QObject::startTimer: QTimer can only be used with threads started with QThread

Die Loesung: Ich habe nun alle Verbindungen im Thread in Qt:QueuedConnection abgeaendert und es scheint zu funktionieren.

Col
Antworten