Seite 1 von 1

Problem mit Sockets in Threads

Verfasst: 15. Oktober 2012 19:03
von Tilman Räger
Hallo,

Ich habe momentan ein kleines Problem mit Threads:

In einer Anwendung starte ich mehrer QTcpSocketServer in jeweils einem eigenen Thread, die (jeweils einem eigenen Port und, ggf. auch einer festgelegen IP) auf Verbindungsanfragen warten. Das ganze klappt auch, lediglich wenn ich versuche auf den zustande gekommenen Socket zu schreiben, erhalte ich eine Fehlermeldung, es könnte kein Objekt in einem Thread erzeugt werden, dessen Parent ein anderer Thread ist. Nach div. Versuchen bin ich allmählich ratlos, vor allem, da die Meldung nicht mit einem von mir erzeugten Objekt zusammen hängt sondern aus der Write-Routine zu stammen scheint.

Beim Versuch, eine abgespeckte Variante des Problems zu erzeugen, wird mir schon die Leseroutine um die Ohren gehauen und das Programm raucht mit einer Assertion ab. Vielleicht kann mir ja jemand einen Tip geben, wie ich das Problem in den Griff bekommen kann und wo mein Denkfehler liegt. Die jeweiligen Klassen sind nicht von den QTcpSocket / QTcpSocketServer Klassen abgeleitet, sondern enthalten Referenzen auf die jeweiligen Objekt. Diese abgespeckte Version habe ich als Zip-File beigelegt - ich hoffe, es hat geklappt :-)

Für alle ernstgemeinten (- d.h. nicht RTFM - das habe ich bereits ;-) ) Antworten schon im Voraus vielen Dank!

Gruss
Tilman (Räger)

Re: Problem mit Sockets in Threads

Verfasst: 15. Oktober 2012 19:26
von Christian81
Ohne genau zu schauen aber Du übergibst dem QThread-Objekt ein Parent aus einem anderen (in diesem Falle den Haupt) Thread. Und dasi st genau das was er anmeckert - der parent ist dann in einem anderen Thread als das Kind - das kann nicht gehen. Also übergibt keinen Parent an den Thread und die Meldung ist weg.

Re: Problem mit Sockets in Threads

Verfasst: 15. Oktober 2012 20:22
von Tilman Räger
Hallo,

Leider bekomme ich auch, wenn ich das Thread-Objekt ohne Parent aufrufe, die folgende Assertion:

ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 97a260. Receiver '' (of type 'QNativeSocketEngine') was created in thread 22d2478", file kernel\qcoreapplication.cpp, line 469

Ich werde den Tipp aber auch mal in der Hauptanwendung ausprobieren, vielleicht habe ich ja auch in der abgespeckten Variante einen weiteren Bug eingebaut ohne es bisher zu bemerken. Diese Assertion (inkl. Absturz) habe ich bisher nämlich noch nicht beobachten können.

Gruss
Tilman (Räger)

Ergänzung:

Nein, auch in der Hauptanwendung erhalte ich weiterhin die selbe Fehlermeldung beim Schreiben auf den Socket - allerdings keinen Absturz. Seltsamerweise werden die Daten jedoch trotzdem übertragen und kommen in der Rufenden Anwendung an - lediglich die darauf folgende Bestätigung bzw. das Schliessen des Ports wird nicht durchgegeben?

Tilman

Re: Problem mit Sockets in Threads

Verfasst: 15. Oktober 2012 20:46
von Christian81
Ok, nochwas - warum überhaupt die QTcpServer in einem eigenen Thread? Bringt absolut gar nichts solange man nicht hunderte von Anfragen in der Sekunde reinbekommt... und wie man die einzelnen Verbindungen in eigene Threads verpackt steht in der Doku.

Re: Problem mit Sockets in Threads

Verfasst: 15. Oktober 2012 21:07
von odt
Hallo Tilman

Auch wenn Du kein RTFM möchtest, empfehle ich Dir dennoch den Artikel

Threads, Events and QObjects http://qt-project.org/wiki/Threads_Events_QObjects

Auch wenn er harmlos anfängt, musste ich ihn (wohl nicht nur aufgrund meines mangelhaften English's) mehrmals studieren. In der Mitte hat's ein paar DON'Ts, die mir in Deinem Code entgegensprangen (add slots to a QThread subclass). Du hast auf der von QThread abgeleiteten Klasse ein sehr grosses Interface. Dies hatte ich "früher" auch und vermischte dadurch ständig die zwei Abläufe (Main- und Sub-Thread). Erst als ich davon abkam, von Thread abzuleiten, habe ich es "in den Griff" bekommen. D.h. ich habe ein von QObject abgeleitetes öffentliches Interface, das einen "untyperisierten" QThread* hat. Dann habe ich einen privaten von QObject abgeleiteten Worker. Dieser Worker "lebt" im Unterthread (entweder im QThread::run erstellt oder via moveToThread). (*Von QThread nur ableiten, wenn der Ablauf nicht event-gesteuert ist.) Phu, kompliziert, wenn Du möchtest, kann ich es morgen etwas "klarer" schreiben.

Nebenbei, die Netzwerk-Funktionalität von Qt nimmt einem schon viel Threading-Arbeit ab. Auch wenn sie ein single-threaded-Interface haben, im Hintergrund lagert Qt die Arbeit in einen Thread aus. Darum muss z.B. für einen QTcpServer oder auch je Connection kein eigener Thread gemacht werden, solange Du ereignissgesteuert arbeitest. Nur die "Berechnung" einer Antwort blockiert den Hauptthread. D.h. vermutlich bräuchtest Du eigentlich gar keine Threads.

Im ST_Target::dataReceived könnte übrigens ein versteckter Bug sein. Wenn Netzwerkmässig eine "Nachricht\n" in zwei Blöcken reinkäme ("Nach" dataReceived "richt\n" dataReceived) hättest Du einen leeren und einen abgeschnittenen Request. Und ein Häcker könnte Dir mehr als 1k Daten senden, wodurch Du über den allozierten Speicher schreibst.

PS: War etwas langsam im Schreiben, dadurch hat sich meine Antwort mit Christian's überschnitten.

Viele Grüsse
Reto

Re: Problem mit Sockets in Threads

Verfasst: 15. Oktober 2012 21:30
von Tilman Räger
Hallo,

Danke erst einmal (trotz dem RTFM ;-) )

Die Sache, nicht von QThread abzuleiten sondern die Arbeit in ein in diesem Thread erzeugtes Objekt zu verlagern scheint mir zunehmend plausibel - ich denke, ich werde mal in diese Richtung hinarbeiten. Zu der Sache mit den Threads überhaupt.
Die Anwendung, an der ich gerade sitze, öffnet eine Reihe von Ports, über die Datenbankabfragen angestossen werden. Meine Überlegung war, diese ganze Arbeit jeweils in einen Thread zu verlagern, da sich bei mehreren Anfragen gleichzeitig diese gegenseitig blockierten. Möglicherweise beruht diese Blockierung allerdings ja auch auf der Datenbankabfrage.
Eine Frage hierzu vielleicht noch:

Wenn ich eine Datenbank definiert habe (z.B. MySQL unter Linux bzw. ODBC unter Windows), kann ich diese Datenbank von mehreren Threads aus gleichzeitig öffnen oder muss ich bei gleichzeitigen Anfragen diese beiden in jeweils eine Query-Objekt auf die selbe Datenbank-Verbindung (QSqlDatabase-Objekt) loslassen. Steht sicher auch irgendwo in der Doku - hab ich bisher aber leider noch nicht gefunden (oder, aufgrund meines nicht überragenden Englisch überlesen - Hinweis auf eine entsprechende Stelle wird dankend angenommen).

Gruss
Tilman

Re: Problem mit Sockets in Threads

Verfasst: 16. Oktober 2012 09:27
von odt
Guete Morge

Ja, die Datenbank-Abfragen blockieren den Thread. Aber das heisst nicht, dass irgendwie Socket's verlohren gehen, sie antworten nur verzögert.

Nein, QSqlDatabase darf nur innerhalb eines Threads verwendet werden:

Threads and the SQL Module http://qt-project.org/doc/qt-4.8/thread ... sql-module

A connection can only be used from within the thread that created it. Moving connections between threads or creating queries from a different thread is not supported.
In addition, the third party libraries used by the QSqlDrivers can impose further restrictions on using the SQL Module in a multithreaded program. Consult the manual of your database client for more information

Viele Grüsse
Reto

Re: Problem mit Sockets in Threads

Verfasst: 16. Oktober 2012 10:21
von Tilman Räger
Hallo,

danke erst einmal für die Information - mus ich überlesen (oder verdrängt haben)
odt hat geschrieben: Ja, die Datenbank-Abfragen blockieren den Thread. Aber das heisst nicht, dass irgendwie Socket's verlohren gehen, sie antworten nur verzögert.
Da das ganze an div. steuerungsrechner gehen soll, sind solche Verzögerungen natürlich höchst unwillkommen :-(
odt hat geschrieben: Nein, QSqlDatabase darf nur innerhalb eines Threads verwendet werden:

Threads and the SQL Module http://qt-project.org/doc/qt-4.8/thread ... sql-module

A connection can only be used from within the thread that created it. Moving connections between threads or creating queries from a different thread is not supported.
In addition, the third party libraries used by the QSqlDrivers can impose further restrictions on using the SQL Module in a multithreaded program. Consult the manual of your database client for more information
Korrigiert mich bitte, falls ich jetzt Mist schreibe :-)
Es sollte also möglich sein, für jede Datenbank-Verbindung, die ich benötige, einen Thread aufzumachen (unter Berücksichtigung der ganzen Problematik s.o.) der vom Öffnen der Datenbank über die Queries den gesamten Datenbank-Verkehr abhandelt. Sprich ein Signal rein mit der Anfrage (SQL-String, etc.), Signal raus mit dem Endergebnis. Dadurch könnte ich verhindern, das andere Verbindung durch die Datenbank-Abfragen blockiert werden. Momentan habe ich den Effekt, das ich
wenn eine Datenabfrage läuft Probleme bekomme, wenn ein 2. Socket aufgemacht werden soll (-> Timeout) . Wenn ich dann noch für jede Datenbank-Verbindung (z.B. definierte ODBC-Connection) genau einen Thread bzw. ein SQLDatabase-Objekt erzeuge, die alle Queries egal von welchem Socket sie angefragt werden, sollten die Probleme hoffentlich minimiert werden.
Oder hab' ich da noch einen Denkfehler drin?

Gruss
Tilman

Re: Problem mit Sockets in Threads

Verfasst: 16. Oktober 2012 10:47
von odt
Wenn Du Timeout's bekommst, dauert die Datenbankabfrage aber wirklich seeeehr lange.

Ich hätte es eher umgekehrt geschrieben: Je Thread wird eine Datenbank-Verbindung geöffnet.

Aber grundsätzlich richtig.

Tip: http://qt-project.org/doc/qt-4.8/networ ... erver.html

Re: Problem mit Sockets in Threads

Verfasst: 16. Oktober 2012 11:01
von Tilman Räger
Hallo,

so ganz sicher, ob es wirklich timeouts sind oder konkurrierende QSqlDatabase-Objekte, weiß ich momentan nicht. Problem ist, ich selbst habe den Fehler bisher aufgrund meiner etwas beschränkten Testmöglichkeiten noch nicht exakt nachstellen können. Tatsache ist, ein Socket wird verbunden - arbeitet - der 2. Socket bekommt Probleme. Es könnte allerdings auch sein, das der 1.Socket die Probleme macht und nicht mehr arbeitet - so genau hat sich der Kunde noch nicht darüber ausgelassen (bzw. ich habe noch keine Protokolle gesehen und die ganze Kommunikation über diesen Fehler ging bisher über Telefon).

Meine momentane Überlegung ist, aufgrund dieses Threads hier, alle Socket-Verbindungen im Hauptthread, die Datenbank-Verbindungen in Einzelthreads auslagern, damit diese nicht blockieren können. Die Ergebnisse kann ich dann via Signal an die im Hauptthread vor sich hin werkelnden Targets verschicken. Via Signal/Slot-Verbindungen sollte es ja möglich sein, QString- bzw. QStringList-Objekte zw. den Threads auszutauschen.

Tilman

Re: Problem mit Sockets in Threads

Verfasst: 27. November 2012 11:56
von Tilman Räger
Hallo,

nachdem lange Zeit Ruhe war, tritt das Problem mit Timeouts bei konkurrierenden Datenbank-Verbindungen wieder auf - ich muss jetzt also tatsächlich die einzelnen Datenbank-Verbindungen in separate Threads auslagern. Dazu hiess es, das man mit dem Definieren von Slots in der von QThread abgeleiteten Klasse vorsichtig sein müsste. Dazu eine Frage:

Wenn ich nun hingehe und in der Thread-Klasse Slots definiere, die lediglich ein Signal an das eigentliche Arbeitsobjekt im Thread weiterleiten (bzw. ich definiere ein Signal in der QThread-Klasse, das mit einem Signal des aufrufenden Programms verbunden wird) könnte ich die Signal-Slot-Verbindungen vom Thread zur aufrufenden Umgebung etwas kapseln. Ist soetwas zulässig oder gibt es da auch Probleme. Vorteil wäre, ich erzeuge im Hauptprogramm den Thread und verbinde die Slots des Threads mit den entsprechenden Signalen (war ja zur Folge hätte, das diese Slots im Hauptthread ausgeführt werden). Intern verbinde ich die Signale des Threads mit den entsprechenden Slots der Arbeiterklassen :-) was dann hoffentlich innerhalb des Arbeitsthreads ausgeführt würde, und bei der Rückgabe der Ergebnisse ähnlich.

Code: Alles auswählen

z.B. 
class WorkingThread : public QThread
{
protected:
    void run(void)
    {
         WorkingObject = new Arbeiterklasse();
         connect(this, SIGNAL(Request(RequestData)), WorkingObject, SLOT(Request(RequestData)));
         connect(WorkingObject, SIGNAL(Result(ResultData)), this, SIGNAL(Result(ResultData)));
         exec();
    }

private: 
    Arbeiterklasse * WorkingObject;

signals:  
    void Request(RequestData);
    void Result(ResultData);
}
als ganz grobe Skizze. Wäre so etwa möglich?

Gruss
Tilman (Räger)

Re: Problem mit Sockets in Threads

Verfasst: 27. November 2012 12:14
von odt
Grundsätzlich ja.
PS: Zur Reproduzierbarkeit hilft vielleicht ein Sleep.

Re: Problem mit Sockets in Threads

Verfasst: 27. November 2012 13:01
von Tilman Räger
Hallo,

läuft :-)

Der Tip mit dem Sleep ist gut - warum ich da nicht selber drauf gekommen bin :roll: - Danke auf jeden Fall mal.

Tilman (Räger)