Delphi-PRAXiS
Seite 3 von 5     123 45      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Die Frage aller Fragen (Sammlung): „Ist das Thread-Safe?“ (https://www.delphipraxis.net/180940-die-frage-aller-fragen-sammlung-%84ist-das-thread-safe-%93.html)

Mikkey 3. Jul 2014 12:46

AW: Die Frage aller Fragen (Sammlung): „Ist das Thread-Safe?“
 
Ich glaube nicht, dass die Spekulation über atomare Instruktionen in Bezug auf die gestellte Frage Sinn macht.

In DP geht es im Wesentlichen um Delphi, also um eine Hochsprache, die Umgebung sollte man generell als Black Box betrachten.

Somit kann man definieren:

Zitat:

Ein System (Programm) ist thread-sicher, wenn sein Zustand und damit die Zustände aller Subsysteme bei parallelen Aufrufen auf definierte Weise ändert.
Dies kann nur dann erreicht werden,

wenn das Programm nur Komponenten verwendet, die ihrerseits thread-sicher sind.
wenn der Zugriff und die Veränderung gemeinsam verwendeter Zustände aus geregelte Weise erfolgt.


P.S.
Übrigens gibt es eine Komponente System.SysUtils.TMultiReadExclusiveWriteSynchroniz er

Olli73 3. Jul 2014 12:55

AW: Die Frage aller Fragen (Sammlung): „Ist das Thread-Safe?“
 
Zitat:

Zitat von stahli (Beitrag 1264258)
Dann wäre doch sinnvoll, z.B. 8 lesende Zugriffe mit
"CriticalSection.EnterRead" gleichzeitig zuzulassen und nur wenn dann ein "CriticalSection.EnterWrite" dazwischen kommen sollte die 8 Leseaktionen zu beenden, den Zugriff wirklich zu sperren und erst danach wieder andere Zugriffe zuzulassen.

Ist meine Überlegung sinnvoll? Oder gar schon so realisiert (in der Hilfe habe ich dazu nichts Genaues gefunden).

Multi-read-Exclusive-write-Synchronizer

stahli 3. Jul 2014 12:58

AW: Die Frage aller Fragen (Sammlung): „Ist das Thread-Safe?“
 
@Mikkey&Olli73

Danke, genau das meinte ich. :thumb:

stoxx 3. Jul 2014 18:19

AW: Die Frage aller Fragen (Sammlung): „Ist das Thread-Safe?“
 
Zitat:

Zitat von Sir Rufo (Beitrag 1264190)
Bei den aktuellen XEON-MultiCore-Boards hat übrigens jede CPU ihren eigenen Speicher.
[*]The memory slots associated with a given processor are unavailable if the corresponding processor socket is not populated.[*]A processor may be installed without populating the associated memory slots provided a second processor is installed with associated memory. In this case, the memory is shared by the processors. However, the platform suffers performance degradation and latency due to the remote memory.

Das ist aber nicht so richtig das was wir wollen. Scheinbar ist diese Architektur den immer weiter verbreiteten virtuellen (Cloud) Lösungen vorbehalten.
Es ging ja darum, gemeinsamenm Speicher gleichzeitig bearbeiten zu können.
Dass jeder Thread seinen eigenen Speicher bekommt, wäre mit jeder Architektur mit unterschiedlichen Variablen nicht sonderlich schwer zu lösen...

Sir Rufo 3. Jul 2014 19:00

AW: Die Frage aller Fragen (Sammlung): „Ist das Thread-Safe?“
 
Zitat:

Zitat von stoxx (Beitrag 1264327)
Zitat:

Zitat von Sir Rufo (Beitrag 1264190)
Bei den aktuellen XEON-MultiCore-Boards hat übrigens jede CPU ihren eigenen Speicher.
[*]The memory slots associated with a given processor are unavailable if the corresponding processor socket is not populated.[*]A processor may be installed without populating the associated memory slots provided a second processor is installed with associated memory. In this case, the memory is shared by the processors. However, the platform suffers performance degradation and latency due to the remote memory.

Das ist aber nicht so richtig das was wir wollen. Scheinbar ist diese Architektur den immer weiter verbreiteten virtuellen (Cloud) Lösungen vorbehalten.
Es ging ja darum, gemeinsamenm Speicher gleichzeitig bearbeiten zu können.
Dass jeder Thread seinen eigenen Speicher bekommt, wäre mit jeder Architektur mit unterschiedlichen Variablen nicht sonderlich schwer zu lösen...

Die Situation bleibt aber doch gleich, oder weißt du auf welchem Core und welchem Riegel der Wert nun zu finden ist? Und wenn ich der Speicherstelle x einen Wert übergebe, dann muss jeder Thread von dieser Speicherstelle genau den geschriebenen Wert auslesen können.

Das war ja auch nur wegen MultiCore und alle teilen sich einen Arbeitsspeicher :)

stoxx 3. Jul 2014 23:09

AW: Die Frage aller Fragen (Sammlung): „Ist das Thread-Safe?“
 
Zitat:

Zitat von Mavarik (Beitrag 1264017)

Nutzt mir hier ein ThreadPool?

Benötige ich meinen Thread immer wieder, füttere ich „Ihn“ also nur mit neuen Daten und setze einen Event? Oder erzeuge ich den Thread jedes mal neu?

ich bin von den klassischen Anwendungen wie ThreadPools oder Ableitungen von der Klasse TThread ganz weg.
Bei mir gibt es eine Klasse "TThreadExecuter" .. die bekommt einfach einen MethodenPointer und ruft zyklisch wie ein Timer einen MethodenPointer der gewünschten Klasseauf. Meist 1 ms mit Sleep(1) .. aber oft ist das gar nicht notwendig.

So wird eine Funktion, die sich meistens ähnlich schimpft wie "CyclicMainJobs/ CyclicThreadProc" in einem anderen Thread aufgerufen.
Bei Bedarf kann der Aufruf auch schnell alternativ von einem "TTimerExecuter" durchgeführt werden, so wird anstatt eines zweiten Threads die Methode einfach von Hauptthread aus mit einem Timer aufgerufen.

Das Ganze hat den Vorteil, dass man auf alle Variablen der Klasse ohne Umstände Zugriffe hat.
Und das Ganze auch mehr zur "asynchronen" Eventverarbeitung tendiert und mit modernen OOP Konzepten gemeinsam harmoniert, als so eine popelige Schleife lediglich in der abgeleiteten Execute Procedure-

Mit einer übergebenen Instanz einer "SyncKlasse" können auch "teil-global" mehrere Objecte miteinander synchronisiert werden.

Das nur mal als "Brainstorming Idee"....

Aufpassen muss man natürlich, und genau drauf achten, welche Methoden von einem anderem als dem Hauptthread aufgerufen werden ..(werden können).. um dann sauber und ohne Fehler entsprechende Abschnitte im Code zu locken..

Dejan Vu 4. Jul 2014 06:56

AW: Die Frage aller Fragen (Sammlung): „Ist das Thread-Safe?“
 
Zitat:

Zitat von stoxx (Beitrag 1264368)
Meist 1 ms mit Sleep(1) ..

An sich eine coole Idee, aber eine blöde Frage: Ist da nicht ein wenig zu viel context switching und overhead im spiel? Ok, 1000x pro Sekunde irgendwas aufrufen, um zu schauen, ob es etwas zu tun gibt, macht den Kohl nun auch nicht fett, aber ich dachte man ist vom Polling weggekommen. Oder habe ich das falsch verstanden?

Und welche Klasse meinst Du mit 'der gewünschten Klasse'? Und ist der 'Methodenpointer' in der 'gewünschten Klasse' nicht ein Verstoß gegen SRP? Schließlich macht die Methode etwas, das eher mit dem Thread bzw. einer Aktion im Thread zu zun hat, als mit der Klasse an sich.

Wie verhält es sich mit dem OCP, wenn Du eine weitere Aktion im Thread ausführen willst? Dann musst Du ja die gewünschte Klasse aufbohren, d.h. erweitern, obwohl Du an der Funktion der Klasse an sich nichts änderst?

Ich finde einen Workerthread(pool) mit Jobs, die man definiert, implementiert und den Arbeitern zur Verarbeitung gibt, immer noch am elegantesten. Man hat einen Pool pro Anwendung und schmeisst da die Jobs rein, fertig... ne, wie sagt der Fachmann :mrgreen: und schon ist der Drops gelutscht.

Mavarik 4. Jul 2014 09:22

AW: Die Frage aller Fragen (Sammlung): „Ist das Thread-Safe?“
 
Zitat:

Zitat von stoxx (Beitrag 1264368)

Bei mir gibt es eine Klasse "TThreadExecuter" .. die bekommt einfach einen MethodenPointer und ruft zyklisch wie ein Timer einen MethodenPointer der gewünschten Klasseauf. Meist 1 ms mit Sleep(1) .. aber oft ist das gar nicht notwendig.

Autsch...

Ich würde die Klasse nicht ThreadExecuter, sondern lieber "TSaugdenAkkuleer" oder "TCPUCycleKiller" nennen...

Dafür gibt es doch Waitfor oder Multisyncwait damit der Thread schläft so lange er nicht gebraucht wird.

Mavarik

PS.: Ich habe mir hierfür eine mini Unit gemacht die so was erledigt.

Delphi-Quellcode:
unit UseMyThread;

interface

Uses System.Classes,System.SysUtils,System.SyncObjs,System.Generics.Collections;

type
  TMyThread = class(TThread)
   Private
      FRunning     : boolean;
      E_Event      : TEvent;
   Protected
      Procedure Execute;override;
   Public
      Constructor Create;
      Destructor Destroy;override;
      procedure Terminate; reintroduce; virtual;
      Procedure MyExecute;Virtual;Abstract;
      Procedure MyFree;Virtual;Abstract;
      Procedure Go;
  end;

  // ProcessorCount


implementation

{ TMyThread }

constructor TMyThread.Create;
begin
  inherited Create(true);

  E_Event := TEvent.Create(NIL,false,false,'');
  FRunning := false;

  FreeOnTerminate := false;
end;

destructor TMyThread.Destroy;
begin
  Terminate;

  inherited;

  try
    MyFree;
  except
  end;

  try
    E_Event.Free;
  except
  end;
end;

procedure TMyThread.Execute;
begin
  while not(Terminated) do
    begin
      try
        FRunning := false; // Atom ?

        E_Event.WaitFor(INFINITE);

        if Terminated then
          exit;

        FRunning := true;
        MyExecute;
      except
      end;
    end;
end;

procedure TMyThread.Go;
begin
  if not(Started) then
    Start;

  E_Event.SetEvent;
end;

procedure TMyThread.Terminate;
begin
  inherited Terminate;
  E_Event.SetEvent;
end;


end.

Dejan Vu 4. Jul 2014 09:56

AW: Die Frage aller Fragen (Sammlung): „Ist das Thread-Safe?“
 
Zitat:

Zitat von Mavarik (Beitrag 1264399)
Ich würde die Klasse nicht ThreadExecuter, sondern lieber "TSaugdenAkkuleer" oder "TCPUCycleKiller" nennen...

Nicht jeder Entwickelt für akkubetriebene Küchenschneidbretter.

Eine Frage zu deinem Code: Hast Du schon mal was von Fail Fast gehört, oder einfach ausgedrückt: "Ordentliches Exception Handling"? Dein "An die Wand gefahren? Merkt doch keiner" ist eventuell schwierig beim Finden von Fehlern.

Noch eine Frage: Wo ist der Mehrwert der Methode 'Go' ggü 'Resume'? Klar, der Aufruf von 'Suspend' ist ein No-Go, aber ansonsten?

Sir Rufo 4. Jul 2014 09:57

AW: Die Frage aller Fragen (Sammlung): „Ist das Thread-Safe?“
 
@Frank

Den Thread im Suspended-Mode zu starten macht irgendwie keinen Sinn, da der doch schon durch den Event wartet.

Ich würde auch nicht
Delphi-Quellcode:
TThread.Terminate
überschreiben, sondern
Delphi-Quellcode:
Tthread.TerminatedSet
, denn das ist schon als
Delphi-Quellcode:
virtual
deklariert und somit zum Überschreiben gedacht.

Die Klasse selber würde ich noch als
Delphi-Quellcode:
abstract
deklarieren (nur wegen der Dokumentation), denn ohne Ableitung ist die ja so nicht lauffähig.

Einfach so die Exception wegfangen ist auch nicht gerade schön, und wenn fangen, dann nur so:
Delphi-Quellcode:
try
  MyExecute;
except
  // Nur spezielle Exceptions fangen, die MyExecute bewusst ausgelöst hat
  // Alle anderen unvorhergesehenen werden durchgereicht
  on E:MyExecuteException do
    begin
    end;
end;
Das hier
Delphi-Quellcode:
try
  E_Event.Free;
except
end;
ist auch unsauber. Wenn meine Klasse nicht funktioniert, dann möchte ich die Fehler um die Ohren geschlagen bekommen.

Bei dem
Delphi-Quellcode:
MyExecute
und
Delphi-Quellcode:
MyFree
sieht das etwas anders aus, da hierfür nicht die Klasse
Delphi-Quellcode:
TMyThread
zuständig ist.

Aber was passiert denn, wenn in
Delphi-Quellcode:
MyExecute
ein übler Fehler passiert (StackOverflow)?
Der Code kann immer wieder ausgeführt werden, ohne jeden Hinweis auf den Fehler.

Das
Delphi-Quellcode:
Terminate
im Destructor ist überflüssig (wird schon implizit vom
Delphi-Quellcode:
inherited
aufgerufen ... ach nee, bei dir ja nicht ;))


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:36 Uhr.
Seite 3 von 5     123 45      

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