![]() |
1 Datei und 2 Prozesse ...
Moin !
Ich habe hier ein kleines Problem und mir fällt einfach keine wirklich gute Lösung ein :? Also ... Ich habe einen kleinen Indy Webserver. Der stellt eine HTML Seite und eine Grafik zur Verfügung. Die Grafik wird alle 20 Sekunden neu geschrieben (ist von einem TChart). Nun muss aber der Webserver selber immer dann die Grafik neu lesen wenn ein Browser die Seite anfordert. Und da entsteht irgendwann das Problem das sich beide Prozesse überschneiden. Nun habe ich mir schon gedacht eine Temp Datei zu schreiben und dann nur noch die Datei per CopyFile auf die richtige Datei zu überschreiben. Aber auch da gibts irgendwann mal das Problem das der Copy Vorgang noch aktiv ist und die Datei gelesen werden soll. Hat jemand ne gute Idee wie man dieses Problem in den Griff bekommt. Denn die beiden Sachen Lesen / Schreiben laufen vollkommen asynchron. Ach ja und die Grafik speichern bei jeder Anforderung durch die Webseite geht auch nicht. Denn da die Grafik jede Sekunde erweitert wird kann ich nur zu bestimmten Zeiten die Grafik wegschreiben. Weiterhin hat man auch wieder das Problem das 2 Browser parallel auf den Server zu greifen und dann ggf. 2x überlappend die Datei geschrieben werden soll. Verzwacktes Problem :gruebel: |
Re: 1 Datei und 2 Prozesse ...
Schreibe doch die Werte in eine DB o.ä. und erzeuge dann das Bild dynamisch bei Anforderung
|
Re: 1 Datei und 2 Prozesse ...
Moin !
Nein das geht aus verschiedenen Gründen nicht. Das ist ein Livelogging und wir wollen zwar demnächst eine Dtaenbank verwenden, aber die Einzelwerte stehen da dann nicht drin. Ich glaube das würde in Summe auch zu viel Zeit kosten jeweils die Grafik neu zu berechnen. |
Re: 1 Datei und 2 Prozesse ...
Reicht es nicht, wenn der lesende Prozess bei Problemen einfach kurz wartet und es dann nochmals probiert? Beim Einlesen öffnest Du dann die Datei mit fmShareDenyNone, sodaß jeder Andere auch auf die Datei zugreifen darf.
Alternativ kopierst Du bei Bedarf deine Datei und liest dann die Kopie ein. |
Re: 1 Datei und 2 Prozesse ...
Und warum lößt du das Ganze nicht Serverseitig?
also das Lesen (an die Brower übergen) z.B. per PHP und dabei auch gleich das tauschen der dort befindlichen Datei. |
Re: 1 Datei und 2 Prozesse ...
Moin !
Der Server ist meine Anwendung. Ich nutze die Indy HTTP Komponente. Und ich wollte das ganze eigentlich eher simpel halten - sprich einfache HTML Seite + Grafik. |
Re: 1 Datei und 2 Prozesse ...
du könntest auch einfach 2 Dateien nehmen
eine wird grad geschrieben und die andere ist für die Ausgabe. und dann wird jeweils gewechselt. |
Re: 1 Datei und 2 Prozesse ...
Hallo,
mal der Versuch einer "schmutzigen" Lösung:
Delphi-Quellcode:
Var
iError : Integer; ms : TFileStream; bOk : Boolean; begin ... ms := TMemoryStream.Create; iError := 0; bOk := False; Repeat try ms.LoadFromFile('Dateiname'); Response.ContentType := 'image/jpeg'; Response.SendStream(ms); Response.SendResponse; bOk := True; except inc(iError); Sleep(500); // probieren, wie lange dauert das Erstellen der Datei gewöhnlich? end; until bOk or (iError > 3); ms.Free; if Not bOk then begin Fehlerbehandlung... end; ... end; |
Re: 1 Datei und 2 Prozesse ...
Moin !
Ich habe das jetzt so gelöst:
Delphi-Quellcode:
Das ist der Code für den Webserver (Anfrage vom Client).
procedure TLV_HTTP_Server.HTTPServer_CommandGet(AContext : TIdContext;
ARequestInfo : TIdHTTPRequestInfo; AResponseInfo : TIdHTTPResponseInfo); var LFilename: string; LPathname: string; begin LFilename := ARequestInfo.Document; if (LFilename = '/') or (LFilename = '/PageResult') then begin LFilename := '/index.html'; end; LPathname := HTMLDir + LFilename; if FileExists(LPathname) then begin while FileInUse(LPathname) do Application.ProcessMessages; AResponseInfo.ContentStream := TFileStream.Create(LPathname, fmOpenRead + fmShareDenyNone); //fmShareDenyWrite); end else begin AResponseInfo.ResponseNo := 404; AResponseInfo.ContentText := 'The requested URL ' + ARequestInfo.Document + ' was not found on this server.'; end; end; Und hiermit überprüfe ich ob die Datei in Benutzung ist:
Delphi-Quellcode:
Bis jetzt geht das ganz gut. Ob es der Weisheit letzter Schluss ist .. Weiss ich noch nicht.
function FileInUse(FileName: string): Boolean;
var hFileRes: HFILE; begin Result := False; if not FileExists(FileName) then exit; hFileRes := CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); Result := (hFileRes = INVALID_HANDLE_VALUE); if not Result then CloseHandle(hFileRes); end; |
Re: 1 Datei und 2 Prozesse ...
Hallo,
eigentlich nix gegen einzuwenden,
Delphi-Quellcode:
könnte aber in ungünstigen Situationen zu einer Endlosschleife werden (wenn z. B. der dateischreibende Prozess sich weghängt und die Datei nicht freigibt).
while FileInUse(LPathname) do Application.ProcessMessages;
|
Re: 1 Datei und 2 Prozesse ...
Jo das stimmt.
Ich muss auch gestehen das ich mit dem Konstrukt noch nicht ganz warm geworden bin ... Aber ich werde das jetzt erstmal so weiter testen. Das Problem an der Sache ... Die Tests sind eher müssig und langwirig ... :| |
Re: 1 Datei und 2 Prozesse ...
Trotdem wäre es m.E. besser serverseitig nur die Werte wegzuschreiben ( u.U in Ringpuffer) und das Bild nur bei Bedarf zu erzeugen.
|
Re: 1 Datei und 2 Prozesse ...
Moin !
Zitat:
Ok man könnte die Grafik dann in einem Thread erzeugen lassen. Aber dann steigt der Aufwand für diese Grafik doch enorm. |
Re: 1 Datei und 2 Prozesse ...
Deshalb ja bei Bedarf. Die Werte werden nur gespeichert und bei Anforderung durch Browser erzeugt.
|
Re: 1 Datei und 2 Prozesse ...
Moin !
Ich muss diesen Fred nochmal aufgreifen. Irgendwie bin ich mit der jetzigen Lösung noch nicht im Reinen.
Delphi-Quellcode:
Sowas widerstrebt mir irgendwie genauso wie Sleep Aufrufe.
while FileInUse(LPathname) do Application.ProcessMessages;
Habe jetzt folgende Idee ... Generell gibt es ja 2 Prozesse die vollkommen asynchron zueinander laufen: 1) Speichern der Webseite Das speichern erfolgt in einen Temp Ordner. Aufgerufen wird das Speichern der Webseite in unserem Programmlauf. Und zwar dann wenn die Grafik definitiv komplett gezeichnet ist. Das klappt aber eh ... Gespeichert wird nur (!) SaveDone = False. SaveDone = Boolwert - zeigt das die Webseite neu im Temp abgespeichert wurde und das Speichern abgeschlossen ist. Nach dem Speichern der Seiteninfos SaveDone = True setzen. Mehr passiert hier nicht - darf auch nicht um den normalen Programmablauf nicht unnötig zu stören. 2) Laden der Webseite aus dem Browser Jetzt kommt der heikele Part. Denn das Laden der Webseite kann ja z.B. theoretisch von 100 Browsern im Sekundentakt erfolgen. Bei jedem Lesen wird das Event HTTPServer_CommandGet ausgelöst. Ich klammere deshalb das ganze Lesen mit dem Inkementieren und Dekrementieren eines Counter - nennen wir ihn mal Z. Also sowas:
Delphi-Quellcode:
Daraus kann man dann folgende Schlüsse ziehen:
INC(Z);
//.... HTTPServer_CommandGet Code DEC(Z); - ist der Counter = 1 dann ist das CommandGet aktuell nur 1x am laufen. - ist der Counter > 1 dann sind gerade mehrere Leseoperationen am Webserver aktiv. Wenn der Counter = 1 ist UND SaveDone = True dann wird das Lesen der Webseite geblockt. Jetzt kopiere ich den Kram vom Temp Ordner ins Webserververzeichnis. Temp Verzeichnis löschen. Block aufheben. SaveDone = False In Code sähe das dann in etwa so aus:
Delphi-Quellcode:
Könnte mir vorstellen das es so ganz gut funktioniert. Genaugenommen ist es zwar immer noch nicht 100%ig, denn wenn genau zwischen "if (Z = 1) AND (SaveDone = True) Then " und "BlockReading := True;" eine neue Leseanforderung kommt, denn könnte es wieder zu Problemen kommen. Aber da unser Webserver dafür eingesetzt wird, eine Chart Grafik darzustellen und wir einen Refresh von 20 Sekunden haben, sollte das verschmerzbar sein.
INC(Z);
if (Z = 1) AND (SaveDone = True) Then begin BlockReading := True; // ... COPY Temp to Webfolder // ... CLEAR Temp folder BlockReading := False; SaveDone := False End; //.... HTTPServer_CommandGet Code DEC(Z); Evtl. könnte man das auch noch etwas abfangen wenn man nach dem INC(Z) erst ein
Delphi-Quellcode:
aufrufen würde.
BlockReading := (Z = 1) AND (SaveDone = True);
Jedenfalls wäre das eine Lösung die ohne grossartige Sleeps oder sonstige Wartezyklen auskäme. Oder habe ich was gravierendes übersehen !? |
Re: 1 Datei und 2 Prozesse ...
Wie machst du das mit der Erzeugung der Grafik-Datei eigentlich genau?
Also angenommen du hast die Datei grafik.png Ich würde es so machen, dass der erzeugende Prozess die Grafik grafik_new.png erzeugt, dann die grafik.png in grafik_old.tmp umbenennt und danach die grafik_new.png in grafik.png umbenennt und die grafik_old.png löscht oder in ein Archiv schiebt. Muss auch nicht grafik_old.png sein, sondern kann ja grafik_TIMESTAMP.png sein damit da nix passiert wenn die grafik_old.png nicht gelöscht werden konnte. Dieses Umbenennen sollte nicht lange dauern, das heisst du kannst beim Lesen der Datei erstmal schauen ob die grafik.png existiert, wenn nicht warten. Am besten zu zählst da einen Counter hoch, weil man davon ausgehen dürfte, dass die Datei nicht länger als zB 2 Sekunden weg sein dürfte. Ist der Counter zu hoch, gibst du ne Fehlergrafik aus, das es Probleme beim Erzeugen der grafik.png gibt. Nicht vergessen bei erfolgreichem Ausgeben den Counter wieder zurückzusetzen falls das -je nach Konstrukt - nötig sein sollte. |
Re: 1 Datei und 2 Prozesse ...
Zitat:
Verwende also einfach eine Semaphore oder innerhalb einer Anwendung (aber unterschiedlichen Threads!) eine 'TCriticalSection'. So schützt Du den Zugriff, sorgst also dafür, das garantiert nur maximal ein Thread die Resource benutzen kann:
Delphi-Quellcode:
uses SyncObjs;
... FileCriticalSection := TCriticalSection.Create; ... // Thread1: ... FileCriticalSection.Enter; Try WriteTheFile; Finally FileCriticalSection.Leave; End; ... // Thread2: ... FileCriticalSection.Enter; Try ReadTheFile; Finally FileCriticalSection.Leave; End; ... |
Re: 1 Datei und 2 Prozesse ...
Moin !
@alzaimar: Und in WriteTheFile / bzw. ReadTheFile würde ich dann meinen Code zum Lesen bzw. Schreiben der Datei packen? Oder darein nur den Counter bearbeiten? |
Re: 1 Datei und 2 Prozesse ...
Das jetzigen Konzept ist wohl so:
Code:
Ich würde die Grafik nur dann erstellen, wenn diese auch wirklich gebraucht wird:
DatenSection TCriticalSection
GrafikSection TMultiReadExclusiveWriteSynchronizer Speichern-Thread: - Enter DatenSection - Daten entgegen nehmen und speichern - Prüfen letzter Grafikerstellungszeitpunkt und ob Daten neue Daten vorhanden, wenn erforderlich - Enter GrafikSection schreiben - Daten lesen - Grafik erstellen - Schreiben Grafikerstellungszeitpunkt - Exit GrafikSection schreiben - Exit DatenSection Webseite-Thread: - Enter GrafikSection lesen - Grafik lesen - Exit GrafikSection lesen
Code:
DatenSection TCriticalSection
GrafikSection TMultiReadExclusiveWriteSynchronizer Speichern-Thread: - Daten entgegen nehmen - Enter DatenSection - Daten speichern - Exit DatenSection Webseite-Thread: - Enter DatenSection - Prüfen letzter Grafikerstellungszeitpunkt und ob Daten neue Daten vorhanden, wenn erforderlich - Enter GrafikSection schreiben - Daten lesen - Grafik erstellen - Schreiben Grafikerstellungszeitpunkt - Exit GrafikSection schreiben - Exit DatenSection - Enter GrafikSection lesen - Grafik lesen - Exit GrafikSection lesen |
Re: 1 Datei und 2 Prozesse ...
Moin !
@Blup: Das mit dem Grafik schreiben ist so eine Sache. Die Grafik steht bereits und ich möchte auch nicht eine neue Grafik berechnen lassen. Das kostet alles Zeit. Ich habe aber jetzt dank alzaimar die Sache mit TCriticalSection versehen. Und siehe da, all meine Probleme haben sich wie von Zauberhand gelöst. Habe schon seit Stunden einen Test am rennen mit mehreren Webbrowsern parallel. Bis jetzt keinerlei Probleme. |
Re: 1 Datei und 2 Prozesse ...
Zitat:
Aber der ganze Beitrag dreht sich doch darum das dies doch alle 20s notwendig sein kann? Und wenn das notwendig ist und alle Webseite-Threads durch die TCriticalSection darauf warten müssen, ist es auch egal durch welchen Thread dies ausgelöst wird. |
Re: 1 Datei und 2 Prozesse ...
Moin !
Die Grafik wird im Sekundentakt upgedatet. Das ist das was die Anwendung eben im Normalfall tut - Kurven plotten. Zitat:
Und weil das TChart die Grafik neu zeichnet wenn man neue Punkte einträgt, muss ich nach dem Eintragen irgendwann mal die Grafik speichern. Mache ich das aus dem Webthread raus, dann kann es sein das die Seite unvollständig gespeichert wird weil sie eben zum Zeitpunkt der JPG Erstellung noch nicht komplett gezeichnet war. Zitat:
|
Re: 1 Datei und 2 Prozesse ...
Dann ist das Hauptproblem das du Daten halten und Darstellung nicht trennst.
Aber wenn es für deinen Anwendungsfall reicht... |
Re: 1 Datei und 2 Prozesse ...
ich hätte es eher so gemacht,
dann sind die Grafiken nicht so lange blockiert
Code:
oder gar so
Webseite-Thread:
- Enter DatenSection - Prüfen letzter Grafikerstellungszeitpunkt und ob Daten neue Daten vorhanden, wenn erforderlich - [b]Daten lesen[/b] - [b]Grafik erstellen[/b] - Enter GrafikSection schreiben - [b]Grafik speichern[/b] - Schreiben Grafikerstellungszeitpunkt - Exit GrafikSection schreiben - Exit DatenSection - Enter GrafikSection lesen - Grafik lesen - Exit GrafikSection lesen
Code:
Webseite-Thread:
- Enter DatenSection - Prüfen letzter Grafikerstellungszeitpunkt und ob Daten neue Daten vorhanden, wenn erforderlich - [b]Daten lesen[/b] - Exit DatenSection - [b]Grafik erstellen[/b] - [b]Enter GrafikSection schreiben[/b] - [b]Grafik speichern[/b] - [b]Schreiben Grafikerstellungszeitpunkt[/b] [color=silver]- [b]Exit GrafikSection schreiben[/b] - Enter GrafikSection lesen[/color] - Grafik lesen - Exit GrafikSection lesen |
Re: 1 Datei und 2 Prozesse ...
Ich hätte überlegt, vor dem Bildupdate kurz die index.html zu ersetzen.
"...wait" und refresh, jede Sekunde Wenn ein Browser in dem Moment des Bilderupdates seine Darstellung aktualisiert, muss er gar nicht auf das Bild zugreifen und wartet 1 Sekunde. Dann das Bild neu speichern und die normale index.html zurückschreiben. Der nächste Browser-Refresh zeigt das wieder Bild an. ... Ach so, der letzte "zulässige" Bildzugriff kann natürlich immer noch Schreibkonflikte verursachen. Dann sollte man den Schreibversuch einfach nochmal wiederholen. Stahli |
Re: 1 Datei und 2 Prozesse ...
Darf ich fragen, um was für ein System es sich hier handelt? Ab Windows Vista bzw. Server 2008 gibt es Transactional NTFS, damit ist das ganze am einfachsten gelöst.
|
Re: 1 Datei und 2 Prozesse ...
Konflikte zwischen schreibender Instanz und mehreren lesenden Instanzen lassen sich nur komplett vermeiden, wenn wirklich alle zugreifenden Threads über eine CriticalSection synchronisiert werden. Soweit ich das verstanden habe, hast du das für die ClientThreads jetzt erreicht. Allenfalls der schreibende Prozess könnte jetzt noch dazwischenfunken.
Mein Lösungsansatz wäre, die neue Datei mit einem anderen Namen zu erstellen und dann dem Web-Server (z.B. über eine WebService-Methode oder den Aufruf einer virtuellen Website) mitzuteilen, dass die neue Datei da ist und er sie umkopieren kann. Natürlich synchronisiert über dieselbe CriticalSection, die auch die ClientThreads nutzen. Damit sind dann alle Zugriffe auf die Datei in einer Hand. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:40 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz