Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   HTTP-Get-Thread NIEMALS zwei gleichzeitig abarbeiten (https://www.delphipraxis.net/200439-http-get-thread-niemals-zwei-gleichzeitig-abarbeiten.html)

Markus Effenberger 19. Apr 2019 17:54

HTTP-Get-Thread NIEMALS zwei gleichzeitig abarbeiten
 
Hallo,

Das Problem wurde leider immer noch nicht gelöst :(

Es wird ein IdHTTPServer betrieben.
Im OnGet kommen z.B. Bestellungen rein, die z.B. einen Bondruck mittels FastReport anstoßen.
Weil sich der Fastreport aktuell auf einem Form der GUI befindet, darf immer nur ein OnGet einzeln abgearbeitet werden.
Also alle OnGet-Ereignisse (insbesondere die Funktion ANTWORTEN_ERZEUGEN) sollen NACHEINANDER abgearbeitet werden.
Wenn ein OnGet durch eine Anfrage ausgelöst wird, und gerade schon ein anderer Thread die Funktion ANTWORTEN_ERZEUGEN abarbeitet, dann soll der neue OnGet-Thread VOR ANTWORTEN_ERZEUGEN warten und die Funktion ANTWORTEN_ERZEUGEN erst beginnen, wenn die Funktion in dem anderen Thread VOLLSTÄNDIG abgearbeitet ist.
Untenstehend ist der Code zu sehen, mit dem ich das versucht habe. Aber es funktioniert irgendwie nicht.
Mein Eindruck ist, dass ein Thread aus der Funktion ANTWORTEN_ERZEUGEN rausspringt bzw fertig ist, obwohl der Druck und/oder die Abarbeitung (z.B. Filtern von Tables und Schleifen durch die Tables) gerade noch stattfindet.

Hat jemand zufällig einen Vorschlag, wie man das Problem lösen kann? Mir gehen allmählich die Ideen aus. Falls das eine Rolle spielt: Es wird Firemonkey genutzt.

Die Ausgaben "Deadlock detected" und "DoubleGet detected" erscheinen ab und zu beide. Wenn ein Client ab und zu mal ein paar Sekunden warten muss wegen der seriellen Abarbeitung wäre das nicht weiter schlimm.

Markus


Code:
procedure TfrmMain.HTTPServerCommandGet(AContext: TIdContext;
                                        ARequestInfo: TIdHTTPRequestInfo;
                                        AResponseInfo: TIdHTTPResponseInfo);
begin

  try


    GUI_Lock_Starten_oder_Warten;
    TThread.Synchronize(nil,
       procedure
       begin
         ANTWORTEN_ERZEUGEN(AContext, ARequestInfo, AResponseInfo); // -> AResponseInfo.ContentText
       end
    );
    GUI_Lock_Aufheben;

  except
    on e:exception do
      begin
        AResponseInfo.ContentText := 'Systemfehlermeldung vom Server: ' + e.Message;
      end;
  end;

end;

procedure TfrmMain.GUI_Lock_Starten_oder_Warten;
var SperreAktiv : Boolean;
    warten : integer;
begin

  try

    SperreAktiv := True;
    while SperreAktiv do
      begin

        try
          if not HTTP_is_working
            then begin
              SperreAktiv := False;
            end else begin
              try
                ButtonDeadlock2.Text := 'DoubleGet detected at ' + DateTimeToStr(Now);
              except
                on exception do begin end;
              end;
            end;
        except
          on e:exception do begin end;
        end;
        warten := RandomRange(150,300);
        Sleep(warten);

      end; // while

    HTTP_is_working := True;
    HTTP_is_working_last_Start := Now;

  except
    on e:exception do begin end;
  end;

end;

procedure TfrmMain.GUI_Lock_Aufheben;
begin
  try
    HTTP_is_working := False;
  except
    on e:exception do begin end;
  end;
end;

procedure TfrmMain.Timer_HTTP_Deadlock_Timeout_prüfenTimer(Sender: TObject);
begin

  try

    if HTTP_is_working then
      begin

        if (Abs(SecondsBetween(HTTP_is_working_last_Start,Now)) > 7)
          then begin
            HTTP_is_working := False; // Deadlock übergehen
            try
              ButtonDeadlock.Text := 'Deadlock detected at ' + DateTimeToStr(Now);
            except
              on e:exception do begin end;
            end;
          end;

      end;

  except
    on e:exception do begin end;
  end;

end;

FaTaLGuiLLoTiNe 19. Apr 2019 18:04

AW: HTTP-Get-Thread NIEMALS zwei gleichzeitig abarbeiten
 
Du könntest dir mal TCriticalSection ansehen.

HolgerX 19. Apr 2019 18:19

AW: HTTP-Get-Thread NIEMALS zwei gleichzeitig abarbeiten
 
Hmm..

Meines Wissens ist jeder Aufruf von HTTPServerCommandGet bereits ein eigener Thread..

Für das Problem mit Warten, bis anderer Thread fertig empfehle ich mal so: TCriticalSection

Eine CriticalSection global in deinem HTTP-Server erzeugen und dann in jedem HTTPServerCommandGet:

(Nur so herunter getippt..)

Delphi-Quellcode:
var
  CS : TCriticalSection; // z.B. im initialization erstellt...

procedure TfrmMain.HTTPServerCommandGet(AContext: TIdContext;
                                        ARequestInfo: TIdHTTPRequestInfo;
                                        AResponseInfo: TIdHTTPResponseInfo);
begin
  CS.Acquire;
  try

    ANTWORTEN_ERZEUGEN(AContext, ARequestInfo, AResponseInfo);

  finally
    CS.Release;
  end;
end;
So führt der erste Thread das ANTWORTEN_ERZEUGEN aus und alle anderen warten (durch Acquire) bis er fertig ist.
Wenn diese fertig ist (Release), kommt der nächste dran....

Durch die Globale Verwendung ist zu mindestens der gleichzeitige Zugriff aus den Threads auf ANTWORTEN_ERZEUGEN verhindert..
Jedoch dürfte es hier noch Probleme mit HauptThread geben, da wir ja immer noch der (nicht über TCriticalSection) erzeugten und vom Formular verwendeten Report haben.

Somit würde ich diesen nicht auf dem Formular platzieren, sondern davon losgelöst in einem eigenen Thread, der auch nur per globaler CriticalSection darauf zugreift...

Markus Effenberger 19. Apr 2019 19:34

AW: HTTP-Get-Thread NIEMALS zwei gleichzeitig abarbeiten
 
Zitat:

Zitat von HolgerX (Beitrag 1430659)
Hmm..

...
Eine CriticalSection global in deinem HTTP-Server erzeugen und dann in jedem HTTPServerCommandGet:

(Nur so herunter getippt..)

Ich hatte zuvor schonmal versucht, das Ganze mit einer CriticalSection zu lösen, aber das hatte leider auch keinen Erfolg. Aber vielleicht lohnt es sich ja mal, die CriticalSection mit der Synchronize-Kapselung zu kombinieren. Ich habe mich diesbezüglich jetzt nochmal in das Thema CS reingelesen und habe jetzt noch eine Verständnisfrage.

Hat jemand genauere Informationen über den Unterschied zwischen "Enter" und "Acquire"?

"Acquire attempts to enter the critical section. It will suspend the calling thread if the critical section is in use by another thread, and will resume as soon as the other thread has released the critical section."

"Call Enter to block all other threads from entering code protected by this critical section until the Leave or Release method is called. Enter calls the Acquire method to bind the critical section to the calling thread."

Da steht also quasi mit "Acquire" wartet man.
Mit "Enter" blockt man und ruft das Warten auf.
Was genau macht es dann für einen Sinn, "nur" Acquire aufzurufen? :wall:


Markus

Klaus01 19. Apr 2019 20:01

AW: HTTP-Get-Thread NIEMALS zwei gleichzeitig abarbeiten
 
..vielleicht bringt dieser Thread etwas Erleuchtung: https://www.delphipraxis.net/178723-...-vs-enter.html

Grüße
Klaus

Markus Effenberger 19. Apr 2019 20:20

AW: HTTP-Get-Thread NIEMALS zwei gleichzeitig abarbeiten
 
Zitat:

Zitat von Klaus01 (Beitrag 1430665)
..vielleicht bringt dieser Thread etwas Erleuchtung: https://www.delphipraxis.net/178723-...-vs-enter.html

In der Tat. Danke!

Olli73 20. Apr 2019 12:35

AW: HTTP-Get-Thread NIEMALS zwei gleichzeitig abarbeiten
 
Hier sind ein paar Einstellungen zu sehen, die wichtig sein könnten; insbesondere "Report.EngineOptions.EnableThreadSafe := True;" und "Report.EngineOptions.UseGlobalDataSetList := False;" denke ich.

Wenn du die Komponenten auf ein DataModule setzt und pro Thread eine Instanz davon erstellst, könntest du auch unabhängig drucken.


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:59 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