AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Delphi Threads, welche Lösung haltet Ihr für eleganter ?
Thema durchsuchen
Ansicht
Themen-Optionen

Threads, welche Lösung haltet Ihr für eleganter ?

Ein Thema von DataCool · begonnen am 27. Jun 2006 · letzter Beitrag vom 29. Jun 2006
Antwort Antwort
Seite 2 von 2     12   
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#11

Re: Threads, welche Lösung haltet Ihr für eleganter ?

  Alt 28. Jun 2006, 12:05
Im Falle der INDYs relativiert sich das aber.

Wir kennen alle die Excpetion "Connection closed gracefully". Ja, ein Ausnahme-bedingung und KEINE Fehlermeldung wie oft irrtümlich vermutet. Eben exakt das was EXCEPTION (auch wenn die DP für dieses Wort nur die halbe Wahrheit als Hint anzeigt ) auch bedeutet !

Du setzt den Timeout der TcpClient Komponente auf einen hohen Wert, zb. eben auch INFINITE. Dann rufts du einfach Readln() auf. Intern wird INDY mit Events auf neue Daten warten (wichtig ist das du auch die TcpClient Thread Methode benutzt, logisch). Dein Terminierungs Event entfällt vollständig !!
Denn wenn du deinen Client Thread extern beenden möchtest dann wird INDY intern den Socket asynchron sofot schließen. Das führt dann innerhalb deines .ReadLn() und somit innerhalb deiner Threadbasierten Schleife zu einer Exception, eg. Ausnahme -> Connection closed gracefully

Deine Aufgabe ist es also nur noch in deine Client Thread Methode das .ReadLn() in einem try except Block zu kapseln. Darin wirst du dann auf die Exception EConnectionClosedGracefully und ETimeout (oä.) speziell reagieren. Bei EConectionClosedGracefully terminierst du deine Threadschleife OHNE den Socket manuell zu schließen (er ist ja schon zu).

Ab diesem Moment wartet dein Thread in ReadLn() per Events, das kann ich für INDY garantieren.

Sooo, fehlt nur noch die Konfiguration des Betriebsystemes. Denn zb. in Win2k wartet das OS viele Stunden bevor es eine Socket Verbindung überprüft ob sie noch vorhanden (lebendig) ist. Sollte also der Server hart die Connection trennen so bekommt der Client dies auf solchen Rechnern erst nach vielen Stunden mit. Auf dieses Problem stießen wir selber erst mit dem Firebird Server. Schau mal im MSN nach dort steht beschrieben wie man in der Registry dieses Keepalive oä. einstellen kann.

Gruß Hagen
  Mit Zitat antworten Zitat
Benutzerbild von DataCool
DataCool

Registriert seit: 10. Feb 2003
Ort: Lingen
909 Beiträge
 
Delphi 10.3 Rio
 
#12

Re: Threads, welche Lösung haltet Ihr für eleganter ?

  Alt 28. Jun 2006, 12:37
Hi Hagen,

wie Du gerade richtig bemerkt hast, verhält sich das ganze bei den Idys etwas anders.
Dort gibt es kein OnReceiveData, somit fällt Deine zuerst Geschilderte Vorgehensweise flach.

Deine Zweite Antwort speziell zu den Indys kann ich nicht ganz nallvollziehen was Du jetzt genau vorschlägst.
Wenn Ich Dich richtig verstanden habe, schlägst Du vor :

IM Thread mit ReadLn(INFINITE) oder einem recht hohen Wert auf Daten zu warten.
Wenn ich den Thread von aussen beenden möchte soll ich einfach die Verbindung des IdTcpClient trennen, so das bei ReadLn eine eine Exception ausgelöst wird.
Und bei entsprechender Exception den Thread dann beeenden ?

Mein Problem bzw. meine Anforderung an den Thread ist, das ich Ihn von aussen beenden kann, ich aber auch mitbekomme wenn er sich selber beendet wenn die Connection aus irgentwelchen Gründen unterbrochen wird(DSL 24 Std. Disconnect, Netzwerkkabel ziehen, Internetverbindung wird getrennt etc.)

Mein Thread hat nämlich noch eine Procedure "Cleanup" die Onterminate zugewiesen ist und in dieser wird ein Event ausgelöst das der Thread beendet wurde.
Auf diesen Event reagiere ich an anderer Stelle, erzeuge den Thread wieder neu falls das Inet noch da ist und es nur ein temporärer Fehler war, oder ich schmeisse ein Fehlermeldung raus.

Um zu erkennen ob die Connection vom Server getrennt wurde, schicke ich selber von Zeit zu Zeit ein "NOOP" zum Server, gibts ne Exception weiß ich das die Connection weg ist.

Wäre Dir für ein kleines Code-Gerüst wie Du das machen würdest sehr dankbar, denn ich setze genauso wie Du an meine Software die Ansprüche das Sie nicht nur funktioniert, sondern das Sie zu effektiv und performance schonend wie möglich programmiert ist

Danke und Gruß Data
Der Horizont vieler Menschen ist ein Kreis mit Radius Null, und das nennen sie ihren Standpunkt.
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#13

Re: Threads, welche Lösung haltet Ihr für eleganter ?

  Alt 28. Jun 2006, 13:44
Du reagierst in deiner Client Thread Methode auf solche ungewollten Trennungen. Dh. im Fall von Indy fängst du diese Exceptions ab und startest nach einem Test ob die Verbindung wiederherstellbar ist entweder einen neuen Client Thread oder setzt den aktuellen fort.

Dein Problem ist im Grunde ein Sessionbasiertes Problem, vollkommen losgelöst von den Client Thread Methoden. Du solltest also die Threadmethode eines "Clients" als eine TCP/IP Connection betrachten und NICHT als ein CLIENT !!

Ein Client ist ein Benutzer/Software und damit eine Session. Eine Session kann mehrere Verbindungen aufbauen, auf mehreren Ports zur verschiedenen IPs etc.pp. Du musst also einen eigenen Überbau (zb. Objekte) schaffen in denen die übergreifenden Variablen/Staties etc.pp. verwaltet werden. In dieser Client-Session werden dann auch die verschiednenen Clientthreads zu einzelnen Verbindungen abgespaltet.

Eine solche Session ist es auch die ein keepalive sendet, also dein besagtes NOOP. Das ist aber im Grunde überflüssig da das Socket Konzept von vornherein es vorsieht das der Programmierer eines Sockets darüber informiert wird das die Verbindung unterbrochen wurde.

Ergo: dein eigentliches Problem ist ein konzeptionelles oder ein Verständnisproblem was ein CLIENT im Sinne INDYs ist und was ein CLIENT in deinem Sinne darstellt.

Du kannst also ein Client als Session Objekt programmieren und dazu viele verschiedene TidTCPClient handler Methoden. Diese Methoden, bzw. threadbasierte Connections können je nach deren Aufgabe ganz differenziert auf eine Funktion zugeschnitten sein. Zb. spaltet die Client Session auf Befehl des Servers eine spezielle TcipClient Methode ab die dann einen Dateidownload durchführt. Eine andere Conection dient wie zb. beim FTP als Kommandokanal.

Es gibt nun ein statisches und ein lebendes Session Management. Statisch bedeutet: Client baut zum Server Verbindung auf, fragt par Daten ab und danach trennt er die Verbindung. Ideales Beispiel für zb. eine Daten Synchronisation, ein Installationstool etc.pp.
Oder es gibt lebendige Sessions. Diese leben die ganze Zeit lang wie es der Benutzer wünscht. Er verbindet einmalig zu einem Server und trennt auch manuell zu einem späteren Zeitpunkt. Die ganze Zeit dazwischen lebt die Session aber es werden je nach Aktion neue Connections aufgebaut, Daten übertragen und diese Connections wieder geschlossen.

Alles andere macht keinen Sinn. Also die INDY TcipClient Connections für so ein Session basiertes System zu benutzen ist unsinnig und kostet Zeit und Resourcen. Ich meine damit das man in EINE TcipClient Methode ALLES was eine Session können muß reinprogrammieren möchte, das geht nicht.

Eine TCP/IP Connection sollte immer "linear sequientiell" aufgebaut sein. Connection erzeugen und öffnen, danach auf Daten warten oder senden, und danach sofort wieder terminieren. Das ist es wie ich es handhabe und damit habe ich die besten Erfahrungen gemacht. Fehler wie "Verbindungsaufbau weil der Server nicht erreichbar ist" werden damit schon im vorhinein beim Connecten eines TCPIPClients abgefangen und nicht innerhalb einer Thread Methode.

Denn bei jeder lebendigen Session tritt das Problem für den Programmierer auf die Daten und Aktion Threadübergreifen synchronisieren zu müssen. Das ist ein schwieriges Problem besonders im Hinblick darauf das die VCL dazwischen hängt, das GUI auch nch gefüttert werden muß und wir als Programmierer NIE wissen können was nun threadsafe oder unsafe ist (also synchronisert werden muß).


Gruß Hagen
  Mit Zitat antworten Zitat
Olli
(Gast)

n/a Beiträge
 
#14

Re: Threads, welche Lösung haltet Ihr für eleganter ?

  Alt 28. Jun 2006, 21:48
Ich stimme soweit Hagens Schilderung zu, möchte aber noch darauf hinweisen, daß man unter Windows ohnehin nicht sagen kann, ob die Wartezeit nun 250ms ist. Es handelt sich eben um kein Echtzeitbetriebssystem. Was man sagen kann ist: in beiden Fällen (WaitFor*Object(s), Sleep) wird Rechenzeit abgegeben und irgendwann nach mindestens der Wartezeit wird der Thread-Scheduler den Thread wieder an die Reihe nehmen. Das hängt aber von diversen Faktoren ab und ist nicht steuerbar.
  Mit Zitat antworten Zitat
Benutzerbild von stoxx
stoxx

Registriert seit: 13. Aug 2003
1.111 Beiträge
 
#15

Re: Threads, welche Lösung haltet Ihr für eleganter ?

  Alt 28. Jun 2006, 22:22
darf ich mal fragen, was Overlappings sind ?
Danke !
Phantasie ist etwas, was sich manche Leute gar nicht vorstellen können.
  Mit Zitat antworten Zitat
Olli
(Gast)

n/a Beiträge
 
#16

Re: Threads, welche Lösung haltet Ihr für eleganter ?

  Alt 28. Jun 2006, 22:58
Zitat von stoxx:
darf ich mal fragen, was Overlappings sind ?
Danke !
Da war der Hagen etwas ungenau. Es gibt sogenannte MSDN-Library durchsuchenOVERLAPPED-Strukturen (MSDN-Library durchsuchenLPOVERLAPPED) in Windows, die man bei bestimmten I/O-Funktionen angeben kann. Dabei geht es um "asynchronous I/O" (also asynchrone Ein-/Ausgabe).

"Overlappings" nennt man das deshalb nicht.

Stell dir folgendes vor. Du hast einen Gerätetreiber (egal für was) und du weißt, daß das Gerät länger braucht um eine Anforderung deinerseits zu bearbeiten. Deshalb entscheidest du dich, daß du eine Anforderung, z.B. ein MSDN-Library durchsuchenReadFile, asynchron abschickst. Im Klartext bedeutet das, daß die Funktion ReadFile sofort zurückkehrt und du dank des hEvent in der OVERLAPPED-Struktur über den Ausgang (Erfolg/Mißerfolg) deiner Anforderung benachrichtigt wirst, sobald sie verarbeitet wurde.
Dafür gibt es natürlich vielerlei Anwendungsmöglichkeiten - die besten vermutlich bei Netzwerkgeschichten, wo Verzögerungen "normal" sind.
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#17

Re: Threads, welche Lösung haltet Ihr für eleganter ?

  Alt 29. Jun 2006, 00:29
Stimmt das war ungenau. Diese Strukturen sind nötig für asynchrone und gepufferte IO Operationen. Das trifft auf das Filesystem API zu, auch auf zb. COM oder USB API und eben auf Sockets. Neben einem Event kann in dieser Datenstruktur noch wesentlich mehr beeinflusst werden. Sockets, COM, USB und oft auch File Operationen die per Events asynrchon signalisiert werden machen meistens nur mit dieser Overlapping Struktur einen Sinn. Das Overlapping hat sich deshalb bei mir so eingeprägt weil es mit Überlappung (zeitlichen Überschneidungen) zu tuen hat, also asynchron ist.

Es geht aber noch weiter !

Zitat:
Stell dir folgendes vor. Du hast einen Gerätetreiber (egal für was) und du weißt, daß das Gerät länger braucht um eine Anforderung deinerseits zu bearbeiten. Deshalb entscheidest du dich, daß du eine Anforderung, z.B. ein ReadFile , asynchron abschickst. Im Klartext bedeutet das, daß die Funktion ReadFile sofort zurückkehrt und du dank des hEvent in der OVERLAPPED-Struktur über den Ausgang (Erfolg/Mißerfolg) deiner Anforderung benachrichtigt wirst, sobald sie verarbeitet wurde.
und ergänzend kommt nun das Threadhandling als Verbesserung hinzu.

Denn stell dir vor das alles machst du in einem Thread. Der Thread wartet nun nicht mehr auf das synchrone Abarbeiten der IO Operation sondern legt sich mit WaitForSingle/MultipleObjects selber schlafen. Er verbraucht so keine Rechenzeit und wird nun statt ständig zu warten bzw. zu pollen direkt vom OS per Event signalisiert das was fertig ist. Und ganz wichtig ! Statt sequientiell nacheinander mehrere solcher IO Operation nacheinander zu erledigen (mit unbekannter Wartezeit) startet er einfach mehrere solcher asynchronen Vorgänge und wartet dann einfach per WaitForMultipleObjects() auf das Eintreffen eines Signales einer dieser IO Operationen. Je nach Dauer dieser Operationen wird er mit der ersten fertigen Operation anfangen. Gerade bei Sockets mit ihren unvorhersagbarem Timing also ideal.

Events, Overlapping, IO Operationen, Semaphore und sogar der Messagequeue machen dann mit Hilfe der Threads so richtig Sinn.

Gruß Hagen
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#18

Re: Threads, welche Lösung haltet Ihr für eleganter ?

  Alt 29. Jun 2006, 00:37
@Olli:

Zitat:
Ich stimme soweit Hagens Schilderung zu, möchte aber noch darauf hinweisen, daß man unter Windows ohnehin nicht sagen kann, ob die Wartezeit nun 250ms ist. Es handelt sich eben um kein Echtzeitbetriebssystem. Was man sagen kann ist: in beiden Fällen (WaitFor*Object(s), Sleep) wird Rechenzeit abgegeben und irgendwann nach mindestens der Wartezeit wird der Thread-Scheduler den Thread wieder an die Reihe nehmen. Das hängt aber von diversen Faktoren ab und ist nicht steuerbar.
Das stimmt, aber im Grunde wollte ich auf was anderes hinaus.

Denn mit der Anwendung von Sleep(250) ist es garantiert das druchschnittlich gesehen der Thread immer 125ms zu spät auf Daten im Socket reagiert, wenn der Socket auch welche empfangen hat. Das ist klassisches intervallgesteuertes Polling und fast genauso ineffizient wie ein direktes Polling ohne Sleep() in einer Threadschleife.

Mit Hilfe der Events und dem Tasksheduler stellt Windows quasi eine zeitlich saubere Synchronisation und Verteilung der Rechnerresourcen sicher. (ja nur unter normalen Umständen wir kennen das ja alle )

1.) wird man dann nur den Socket lesen wenn auch wirklich Daten eingegangen sind und nicht alle 250ms sinnlos pollen

2.) ist die Reaktionszeit bis der Thread den Socket ausliest immer identisch und auf jeden Falle keine 125ms, die zeitlichen Verschiebungen bis ein Thread auf ein signalisertes Event reagieren kann liegt nur im Tasksheduler begründet und kann fast schon vernachlässigt werden.

Vielleicht ein Beispiel:

Das Nachschauen ob in einem Socket Daten vorliegen dauert 10 ms.
Der Server schickt seine Daten erst nach 1000ms an den Client.

Wir haben nun Methode 1. den Thread der 250ms per Sleep wartet und danach einfach testet ob Daten im Socket liegen.

Dieser Thread wird also exakt 4 mal Sleep(250ms) aufrufen und 3 mal wird er den Socket sinnlos pollen da noch keine Daten vorhanden sind. Macht also 4 * 250 + 3 * 10 ms = 1030ms minimal an Zeit. Dabei wird der Thread aber auch 4 mal einen Taskswitch miterleben was auch Zeit kostet. Im ungünstigesten Falle könnte es aber auch sein das just in dem Moment wo der Thread Sleep(250) aufruft Daten im Socket landen. Ergo der Thread wartet nochmals zusätzliche 250ms sinnlos und liest erst danach den Socket aus. Im Worstcase ergibt sich also 1280ms.

Nun Method 2 per Events.

Der Thread liest den Socket per Overlapped IO aus. Er ruft also Socket_Read() auf und diese Funktion kehrt sofort zum Thread zurück. Nun wartet der Thread per WaitForSingleObject() das Socket_Read() X Bytes an Daten empfangen hat. Das dauert also exakt 1000ms weil ja der Server diese Zeit benötigt. In dieser Zeit schläft der Thread, er wird also das System nicht weiter belasten. Fazit: 1000ms wartezeit ohne das System weiter zu belasten.

Gut, diese 30ms weniger scheinen im ersten Moment nicht viel aber jetzt wollen wir das dieser Thread gleichzeitig von 3 Servern Daten abholen soll. Und diese 3 Server haben unterschiedliche Antwortzeiten.

Sagen wir mal 1000ms, 1500ms und 1750ms.

Der Thread nach 1. Methode wird erstmal auf Server 1 warten, macht 1000ms dann auf Server 2 macht + 1500ms und dann Server 3 + 1750ms, summa summarum also 4250ms.

In der Methode 2. wird er alle 3 Server ansprechen und dann auf alle 3 Events der Sockets warten. Nach 1000ms antwortet der Server 1 und er Thread liest dessen Daten. Nach weiteren +500ms antwortet schon Server 2 und nach weiteren 250 ms der Server 3. Logisch 1000ms + 500ms = 1500ms + 250ms = 1750ms. Also schon anch 1750ms hat der Thread alle Daten der 3 Server abgearbeitet. Gut, man könnte auch 3 Threads erzeugen die separat warten, aber wozu wenn es auch so geht ?

Gruß Hagen
  Mit Zitat antworten Zitat
omata

Registriert seit: 26. Aug 2004
Ort: Nebel auf Amrum
3.154 Beiträge
 
Delphi 7 Enterprise
 
#19

Re: Threads, welche Lösung haltet Ihr für eleganter ?

  Alt 29. Jun 2006, 00:55
Hallo negaH,

ich möchte nochmal eine Frage zu einem Satz stellen, der hier nur mal so nebenbei viel...

Zitat von negaH:
Sooo, fehlt nur noch die Konfiguration des Betriebsystemes. Denn zb. in Win2k wartet das OS viele Stunden bevor es eine Socket Verbindung überprüft ob sie noch vorhanden (lebendig) ist. Sollte also der Server hart die Connection trennen so bekommt der Client dies auf solchen Rechnern erst nach vielen Stunden mit. Auf dieses Problem stießen wir selber erst mit dem Firebird Server. Schau mal im MSN nach dort steht beschrieben wie man in der Registry dieses Keepalive oä. einstellen kann.
Warum bekommen die INDY-Komponenten also die Clients nicht mit, wenn sich der Server abmeldet bzw. deaktiviert wird? Bei den alten Socket-Komponenten war das kein Problem. Warum muss für die INDYs jetzt etwas in der Registry geändert werden und für die Socket-Komponenten nicht?

Vielleicht kann da ja nochmal einer drauf eingehen.
Oder mal konkret erwähnen, wie man auch die INDYs dazu bewegen kann.

Gespannte Grüsse
Thorsten
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#20

Re: Threads, welche Lösung haltet Ihr für eleganter ?

  Alt 29. Jun 2006, 01:08
Ja das ist ein sehr kourises Verhalten das wir feststellten.

Wir installierten das erste mal Firebird und fingen an uns Gedanken zu machen wie wir die Connection IDs zu einem angemeldeten User umwandeln können und darauf basierend eine eigene Table mit User Verwaltungsdaten füttern könnten. Wichtig dabei war es das in einem Netzwerk ein Benutzer immer nur einmal angemeldet sein durfte.

Das ganze kann nur funktionieren wenn auf Serverseite korrekt erkannt werden kann wenn ein Benutzer sich abgemeldet hat, egal ob aktiv oder indem er den Rechner hart ausschaltet oder sogar indem er das Netzwerkkabel rauszog.

Tja es stellte sich heraus das der Firebird Server nicht auf solche verlorene Verbindungen reagierte, sondern eher das diese Connections ne halbe Ewigkeit aufrecht erhalten blieben.

Es lag an der Konfiguration des Win2k Servers und dessen Sockets. Sie überprüften eine Verbindung nur alle 12 Stunden !! Im MSN kann man nachlesen das dieser Wert von MS absichtlich so hoch gewählt wurde um Netzwerk Resourcen zu sparen.

Wird im TCP/IP eine Verbindung sauber geschlossen so kann dieses Verhalten NICHT auftreten, denn für diesen Zweck gibts im TCP/IP ein Kommando. Nur wenn man das Kabel zieht tritt dies auf. Denn um sowas zu erkennen muß man die stehende Verbindung von Zeit zu Zeit aktiv checken, zb. ein Ping oder ähnliches (wie oben ein NOOP als Keepalive).

[edit]
achso ganz vergessen dieses Verhalten ist unabhängig von INDY oder anderen Socket Komponenten, sondern einfach ein quasi unlösbares Problem jeder TCP/IP Verbindung. Unlösbar wenn man nicht AKTIV die stehenden Verbindungen dauernd überprüft. Denn beim gezogenen Netzwerkkabel, bei hart getrennter INet Verbindungen durch Provider etc.pp. haben die geöffneten Sockets keinerlei Chance mehr über das Schließen des nun nicht mehr ansprechbaren Host informiert zu werden. (wie auch ? )
[/edit]

Gruß Hagen
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 2     12   


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:24 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz