Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   1 Datei und 2 Prozesse ... (https://www.delphipraxis.net/130535-1-datei-und-2-prozesse.html)

moelski 10. Mär 2009 06:51


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:

mkinzler 10. Mär 2009 06:53

Re: 1 Datei und 2 Prozesse ...
 
Schreibe doch die Werte in eine DB o.ä. und erzeuge dann das Bild dynamisch bei Anforderung

moelski 10. Mär 2009 07:05

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.

alzaimar 10. Mär 2009 07:08

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.

himitsu 10. Mär 2009 08:44

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.

moelski 10. Mär 2009 08:48

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.

himitsu 10. Mär 2009 09:38

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.

nahpets 10. Mär 2009 09:46

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;

moelski 10. Mär 2009 10:07

Re: 1 Datei und 2 Prozesse ...
 
Moin !

Ich habe das jetzt so gelöst:

Delphi-Quellcode:
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;
Das ist der Code für den Webserver (Anfrage vom Client).

Und hiermit überprüfe ich ob die Datei in Benutzung ist:
Delphi-Quellcode:
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;
Bis jetzt geht das ganz gut. Ob es der Weisheit letzter Schluss ist .. Weiss ich noch nicht.

nahpets 10. Mär 2009 10:13

Re: 1 Datei und 2 Prozesse ...
 
Hallo,

eigentlich nix gegen einzuwenden,
Delphi-Quellcode:
while FileInUse(LPathname) do Application.ProcessMessages;
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).

moelski 10. Mär 2009 10:14

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 ... :|

mkinzler 10. Mär 2009 10:15

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.

moelski 10. Mär 2009 10:21

Re: 1 Datei und 2 Prozesse ...
 
Moin !

Zitat:

und das Bild nur bei Bedarf zu erzeugen
Das würde bedeuten das ich aber alle x Sekunden eine komplett neue Grafik erzeugen muss und die Werte eintrage. Das kostet imens Zeit. Wir haben Geräte die senden alle 62,5 Millisekunden. Da kommt man dann schnell in Nöte was die Berechnung angeht.
Ok man könnte die Grafik dann in einem Thread erzeugen lassen. Aber dann steigt der Aufwand für diese Grafik doch enorm.

mkinzler 10. Mär 2009 10:26

Re: 1 Datei und 2 Prozesse ...
 
Deshalb ja bei Bedarf. Die Werte werden nur gespeichert und bei Anforderung durch Browser erzeugt.

moelski 11. Sep 2009 05:12

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:
while FileInUse(LPathname) do Application.ProcessMessages;
Sowas widerstrebt mir irgendwie genauso wie Sleep Aufrufe.

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:
INC(Z);
  //.... HTTPServer_CommandGet Code
DEC(Z);
Daraus kann man dann folgende Schlüsse ziehen:
- 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:
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);
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.

Evtl. könnte man das auch noch etwas abfangen wenn man nach dem INC(Z) erst ein
Delphi-Quellcode:
BlockReading := (Z = 1) AND (SaveDone = True);
aufrufen würde.

Jedenfalls wäre das eine Lösung die ohne grossartige Sleeps oder sonstige Wartezyklen auskäme.

Oder habe ich was gravierendes übersehen !?

Angel4585 11. Sep 2009 07:00

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.

alzaimar 11. Sep 2009 07:08

Re: 1 Datei und 2 Prozesse ...
 
Zitat:

Zitat von moelski
Ich klammere deshalb das ganze Lesen mit dem Inkementieren und Dekrementieren eines Counter - nennen wir ihn mal Z.

Fast richtig. Nur das 'Z' nicht threadsicher ist. Wenn die Threads gleichzeitig Z verändern, kippt dieses System. Garantiert.
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;
...

moelski 11. Sep 2009 07:16

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?

Blup 11. Sep 2009 10:23

Re: 1 Datei und 2 Prozesse ...
 
Das jetzigen Konzept ist wohl so:
Code:
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
Ich würde die Grafik nur dann erstellen, wenn diese auch wirklich gebraucht wird:
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

moelski 11. Sep 2009 10:41

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.

Blup 11. Sep 2009 10:59

Re: 1 Datei und 2 Prozesse ...
 
Zitat:

Zitat von moelski
Die Grafik steht bereits und ich möchte auch nicht eine neue Grafik berechnen lassen.

Wenn die Grafik niemals neu berechnet wird, brauchts auch keine TCriticalSection.
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.

moelski 11. Sep 2009 11:04

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:

Aber der ganze Beitrag dreht sich doch darum das dies doch alle 20s notwendig sein kann?
richtig. Die Grafik verändert sich laufend und drum muss alles 20 Sekunden ein neues JPG der Grafik geschrieben werden.
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:

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.
Nicht so ganz. s.o.

Blup 11. Sep 2009 12:33

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...

himitsu 11. Sep 2009 12:37

Re: 1 Datei und 2 Prozesse ...
 
ich hätte es eher so gemacht,
dann sind die Grafiken nicht so lange blockiert
Code:
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
oder gar so
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

stahli 11. Sep 2009 13:37

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

Apollonius 11. Sep 2009 14:19

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.

fajac 11. Sep 2009 14:20

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