Seite 1 von 1

[gelöst] Problem mit blockender Funktion im Slot

Verfasst: 25. November 2010 21:53
von dazedly
Hallo Leute.

Ich habe ein sehr umfangreiches, auf Qt basierendes, RPC Protokoll geschrieben. Ich kann damit bei Bedarf blockieren und warten, bis die Antwort von meinem Server kommt. Das blockeren übernehme ich mit QEventLoop. Es funktioniert fast alles wie ich es auch erwarte und wünsche, ABER wenn ich meine Abfrage in einem SLOT ausführe, scheint es mir meine anderen Signale zu blockieren. Mein QTcpSocket sendet readyRead nicht mehr. Ich habe zum testen mein tcpsocket auch mal in einem Thread laufen lassen, leider ohne Besserung.
Ich habe jetzt wirklich alles ausprobiert.

* Synchrone Abfrage an irgendeiner Stelle im Code (bis auf SLOTS) >> funktioniert wunderbar.
* Synchrone Abfrage in einem SLOT >> readyRead wird nicht mehr ausgelöst.

Re: Problem mit blockender Funktion im Slot

Verfasst: 26. November 2010 08:13
von solarix
hmm.. drei Dinge:

1. "Warten"
dazedly hat geschrieben:Ich kann damit bei Bedarf blockieren und warten, bis die Antwort von meinem Server kommt. Das blockeren übernehme ich mit QEventLoop.
Meinst du das so ähnlich wie bei QTcpSocket: es gibt Signals aber bei bedarf kann auch "waitFor......()" genommen werden? Da startest du aber nicht eine NEUE Eventloop, oder? Dafür müsstest du einfach eine wait-Methode machen, welche mit processEvents() noch Signals durchlässt.
Du kannst im Sourcecode von Qt auch abschauen, denn:
http://doc.qt.nokia.com/4.7/qtest.html#qWait
macht sowas bereits.

2. "Signal in Slot"
dazedly hat geschrieben: Es funktioniert fast alles wie ich es auch erwarte und wünsche, ABER wenn ich meine Abfrage in einem SLOT ausführe, scheint es mir meine anderen Signale zu blockieren.
Wie soll denn dein Programm ein Signal versenden, wenn du gerade was tust (=dich in einem Slot und nicht in der Eventloop befindest)? Mit anderen Worten: deine Beobachtung ist natürlich richtig und kann auch gar nicht anderst sein...

2. "Thread ist gescheitert"
dazedly hat geschrieben: Ich habe zum testen mein tcpsocket auch mal in einem Thread laufen lassen, leider ohne Besserung.
Dann würde ich sagen: du hast das falsch gemacht :wink:.. aber ohne Code können wir natürlich nicht sagen was. Meine Vermutung wäre, dass du "moveToThread()" vergessen hast..

hth!

Verfasst: 26. November 2010 10:26
von dazedly
Ich könnte das tcp socket blockieren lassen, aber wie ich schon geschrieben habe, soll das bei Bedarf passieren. Der TcpSocket soll immernoch auf Nachrichten vom Server lauschen. Ich benötige eine bidirektional Verbindung und diese darf nicht blockieren. Ich verwalte meine Pakete in einer Map, welche nach den RequestIDs der Pakete indiziert ist. Wenn etwas neues kommt, überprüfe ich, ob es eine Antwort auf eines meiner Pakete ist. Der Server kann genauso wie der Client Requests schicken.


Zum Thema Thread:
Da ich die Klasse in der das QTcpSocket erstell wird, von QThread abgeleitet hatte, bin ich auch davon ausgegangen, dass die Signale mit dazu gehören. Ich werde das jetzt noch einmal versuchen, aber habe nicht die große Hoffnung.

Nachtrag:

Hier geht es um eine permanente Verbindung. Die Clients müss sich beim Server Athentifizieren und bekommen und laufen dort in einem eigenen Thread. Der Thread ist ein Objekt, welches wieder abgeleitet ist von QThread. In diesem Objekt sind die Benutzerdaten des Clients gespeichert und dieser hat nur auf bestimmte Funktionen zugriff. Alle Threads werden von einer Map verwaltet und über diese können bei Bedarf die Clients miteinander synchronisiert werden. Der Server funktioniert ohne Probleme und macht genau das, was er soll.

Verfasst: 26. November 2010 10:45
von solarix

Code: Alles auswählen

Client::getInstance()->sendRonsPacket(bla);
   
   this->loop->exec();

   return this->result; 
Ein QObjekt gehört zu einer Eventloop. Wenn du eine neue machst, hat die richtige (die im main() erstellte QApplication) ja keine Chance mehr, Events zu verarbeiten.
Die Lösung dazu habe ich unter Punkt 1. bereits beschrieben: du musst eine eigene Wait-Methode schreiben so im Stil:

Code: Alles auswählen

 do {
   while(QCoreApplication::hasPendingEvents()) 
      QCoreApplication::processEvents(); // ermoegliche readyRead()
   seep(nur_ganz_kurz_z_B_10ms)
   pruefeEndedesRPC()
 } while (rpc_not_finished or timeout)
Da ich die Klasse in der das QTcpSocket erstell wird, von QThread abgeleitet hatte, bin ich auch davon ausgegangen, dass die Signale mit dazu gehören. Ich werde das jetzt noch einmal versuchen, aber habe nicht die große Hoffnung.
Das gibt alle paar Wochen Probleme hier... Lesen-Lesen-Lesen (Doku und hier im Forum).
Eine elegante Methode ist: http://labs.qt.nokia.com/2006/12/04/thr ... -headache/
Hier stimmen dann auch deine Annahmen...

Auf der Serverseite (QTcpServer) ist zu beachten, dass QTcpServer sich selbst als Parent setzt und daher ein "moveToThread()" fehlschlägt. Also zuerst "socket->setParent(NULL)" und danach moveToThread().
Wenn du die letzten beiden Sätze nicht verstehst, ist es noch zu früh für Multithreading... dann empfehle ich dir, Threads ausserhalb deines Projektes in einem kleineren Rahmen zuerst in Betrieb zu nehmen.

hth!

Verfasst: 26. November 2010 12:53
von dazedly
Ich habe meinen Fehler gefunden....

Code: Alles auswählen

connect(this->tcpSocket,SIGNAL(readyRead()),SLOT(readDataSlot()),Qt::DirectConnection);
zu:

Code: Alles auswählen

	connect(this->tcpSocket,SIGNAL(readyRead()),SLOT(readDataSlot()),Qt::QueuedConnection);

Vielen Dank für deine Hilfe und deine Lösungsvorschläge haben mich in das Thema Qt noch weiter hinein befördert, auch wenn jeder versuchte Fix völlig unnötig war.

Verfasst: 26. November 2010 16:46
von solarix
Schön, dass es läuft, aber noch einen Tipp:

Lass den Verbindungstyp komplett weg.. die "Qt::AutoConnection" (Default-Argument) macht dann automatisch das richtige (Direct im gleichen Thread, Queued in unterschiedlichen Threads).

hth!