Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi CriticalSection nötig ? (https://www.delphipraxis.net/13044-criticalsection-noetig.html)

DataCool 10. Dez 2003 00:23


CriticalSection nötig ?
 
Hi Leute,

kurz ne Frage zur CriticalSection:

1. Nehmen wir mal an ich habe innerhalb eines Threads eine private StringListe.
2. Andere Threads können nicht direkt auf die Stringlist zugreifen, sondern nur über den Methode :
Code:
procedure TmyThread.add(Command : String);
begin
   fCmdList.Add(Command);
   if Suspended then
     resume;
end;
3. Die StringList enthält eine Liste von Befehlen die sequentiell abgearbeitet werden sollen, d. h. neue Befehle am Ende hinzufügen und immer das Element 0 in der liste ist der aktuelle Befehl und wird nach der verarbeitung gelöscht.

4. Der Execute Coode des Threads sieht folgendermaßen aus :
Code:
procedure TmyThread.Execute;
begin
   repeat
      while fCmdList.Count > 0 do begin
         doCommand;;
         if fChatList.Count > 0 then // nötig ??
           sleepEx(100,true);
      end;
      // nichts mehr in der Warteschlange --> Thread schlafen legen
      Suspend;
   until Terminated;
end;
So, jetzt meine Fragen :

Da TStringList ja nicht Thread sicher ist, muss ich hier mit CriticalSection arbeiten ?
Obwohl ich von aussen nur immer Elemente am ende der liste hinzufüge ?
Wenn ich mit CriticalSection arbeite, dann nur bei Delete der stringliste oder auch beim Add ?

Irgentwo hab ich mal gelesen das CriticalSections immer global deklariert werden müssen, damit auch alle threads blockiert werden ?!
Ist das richtig ?
Kann ich nicht innerhalb meines Threads als private Var eine CS erzeugen ? Ich schütze ja auch nur das add und Delete der ebenfalls internen stringliste. Und der Zugriff geschieht ja nicht direkt von anderen Threads sondern wie oben beschrieben mit der Add-Methode des Threads.

Letze und abschliessende Frage :

Ist der Construkt im obringen OnExecute praktikabel, um eine Liste in einem Thread abzuarbeiten und wenn nichts zur verarbeitung da ist, soll der Thread schlafen, wenn was hinzugefügt wird soll er aufwachen.

Danke,

Data

choose 10. Dez 2003 01:01

Re: CriticalSection nötig ?
 
Zitat:

Zitat von DataCool
kurz ne Frage zur CriticalSection:

Auch nur ne kurze Antwort, will langsam ins Bett ;)

Zitat:

Da TStringList ja nicht Thread sicher ist, muss ich hier mit CriticalSection arbeiten ?
ja

Zitat:

Obwohl ich von aussen nur immer Elemente am ende der liste hinzufüge ?
obwohl Du "von außen" arbeitest. Was ändert der schlichte Aufruf der Methode?

Zitat:

Wenn ich mit CriticalSection arbeite, dann nur bei Delete der stringliste oder auch beim Add ?
Auch bei Add.

Zitat:

Irgentwo hab ich mal gelesen das CriticalSections immer global deklariert werden müssen [...]?! Ist das richtig ?
Nein.

Zitat:

Kann ich nicht innerhalb meines Threads als private Var eine CS erzeugen?
Geeignete Lösung und performanter als alle Listen über ein gemeinsames Synchronisationsobjekt zu steuern.

Zitat:

Ist der Construkt im obringen OnExecute praktikabel?
Ideal wäre ein FiFo-Puffer, der den Thread (caller von Pop) solange schlafen legt (ich auch gleich ;)) bis wieder ein neues Element dem Puffer zugefügt worden ist (Push).
Schau mal unter in der OH unter Semaphore und Signal im Zusammenhang von Synchronisationsobjekten nach. Das Szenario, was Du beschreibst, nennt man iÜ Bei Google suchenthread producer consumer.

Rumpi 10. Dez 2003 07:40

Re: CriticalSection nötig ?
 
Hi,

ich arbeite dann immer mit TThreadList.
Das ist der sicherste Weg.

Delphi-Quellcode:
  with AThreadList.LockList do // -> TList Thread save
  try



  finally
    AThreadList.UnLock;
  end;

choose 10. Dez 2003 08:07

Re: CriticalSection nötig ?
 
Zitat:

Zitat von Rumpi
Das ist der sicherste Weg.

Leider nicht, selbst wenn die Datenklasse (ob nun in Form eines FiFo-Puffers oder einer Liste) selbst den kritischen Bereich vor gegenseitigem Ausschluss schützt, können immernoch Verklemmungen (Deadlocks) auftreten, sofern die Operationen nicht atomar sind (hold and wait), also gegenseitige Abhängigkeiten existieren und keine Ordnung (zB in Form einer Hierarchie) vorliegt...

Das Thema Threads hat mehr Fallstricke als die Erzeugung von, Warten auf und Benachrichtigung zwischen ihnen!

DataCool 10. Dez 2003 09:55

Re: CriticalSection nötig ?
 
@Choose:

Erstmal danke für Deine Antwort, hat mir schon sehr geholfen.

Bei dem Thema Threads gibt es ja auch verdammt viel Lösungsansätze und keiner ist die Patent-Heilmethode :mrgreen:

Ich habe Deinen Link zum Thema FiFo-Puffer etwas näher betrachtet, dazu noch eine Frage :

Ist das nicht genau das Prinzip, was ich da konstruiert habe ?

Die OnExecute-Methode legt sich schlafen, wenn nichts mehr zur Verarbeitung da ist und die Add Methode weckt den Thread wieder auf, falls er schläft.

Wo ist der Unterschied ?

Gruß Data

choose 10. Dez 2003 10:07

Re: CriticalSection nötig ?
 
Der Unterschied leigt, wie häufig in der OOP, bei der Zuständigkeit: Den Thread hat es nicht zu interessieren, dass er "schlafen gelegt wird". Auch das Locking sollte nicht in seinen Zuständigkeitsbereich fallen.
Idealerweise ist diese Verhalten vollkommen transparent, etwa in der Form:
Delphi-Quellcode:
procedure TMyThread.Execute;
var
  myItem: TMyClass;
begin
  Assert(Assigned(FFifoData), 'FiFo-Buffer not assigned');

  while not Terminated do
  begin
    myItem:= FFifoData.Pop;
    DoSthWidth(myItem);
  end;
end;
bzw
Delphi-Quellcode:
procedure TMyTread.AddItem(const AnItem: TMyClass);
begin
  if not Assigned(AnItem) then
    raise EItemNotAssignedError.Create(ItemNotAssigned);

  Assert(Assigned(FFifoData), 'FiFo-Buffer not assigned');
  FFifoData.Push(AnItem);
end;
Das Blockieren eines Clients (Aufrufer), der die Nachricht Pop schickt, sowie dessen anschließende Fortsetzung, sobald ein Element dem Puffer hinzugefügt worden ist, übernimmt nach diesem Muster vollständig der Puffer.

Problematisch ist in diesem Zusammenhang lediglich der Fall, dass der Thread beendet werden soll, jedoch kein weiterer Eintrag im Puffer vorhanden ist. Hierfür könnte eine spezielle Methode des Puffers eingesetzt werden, der alle Consumer (den Thread) weckt, ohne ein Element als Rückgabewert von Pop zur Verfügung zu stellen. Denkbar ist der Wurf einer Exeception EBufferClosed, die dann in Execute adequat behandelt werden sollte:
Delphi-Quellcode:
procedure TMyThread.Execute;
var
  myItem: TMyClass;
begin
  while not Terminated do
  try
    myItem:= FFifoData.Pop;
    DoSthWidth(myItem);
  except
    on EBufferClosed do
      Terminate;
  end;
end;


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