Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   TDictionary und TThread (https://www.delphipraxis.net/203520-tdictionary-und-tthread.html)

MicMic 26. Feb 2020 00:37

Delphi-Version: 10.3 Rio

TDictionary und TThread
 
Hallo,
Ich kenne das Problem, weiß aber nicht wie man es löst.
Also ich habe ein TThread der 2x (also für 2 x TMyComp) läuft.
Soweit klappt eigentlich alles. Natürlich aber nicht mit einem TDictionary.
Ich kürze mal ab.
Delphi-Quellcode:
TMyThread = Class(TThread)
  Protected
  Procedure Execute; Override;
End;

TMyComp = Class
  CThread : TMyThread;
End;

Var dic : TDictionary // von überall verwendbar

Procedure TMyThread.Execute;
Begin
  If dic.ContainsKey(lowercase(daten)) = False Then
  Begin
    ...code
    dic.Add(lowercase(daten),wert)
    // Code+dic.add eigentlich in einer weiteren Prozedur
  End Else
  Begin
    ...code
    dic.TryGetValue(lowercase(daten),wert);
    // Code+dic.add eigentlich in einer weiteren Prozedur
  End;
End;
TDictionary ist <string><string>. Jedenfalls wird aus den beiden Komponenten (TMyComp) was geladen und diese Daten will ich mit einem einzelnen TDictionary abspeichern. So könnte Komponente1 und Komponente2 stets die Daten aus dem TDictionary abfragen und sie müssten nicht die Daten laden. Das beißt sich natürlich, sprich wenn 1. Thread ein TDictionary halb abgespeichert hat und der 2. Thread meint, er ist schon komplett vorhanden. Das geht natürlich nicht. Natürlich bekam ich auch schon ein Fehler "Duplikate nicht erlaubt". Was natürlich klar ist, wenn hier ein neuer TDictionary Eintrag noch nicht von Komponente1 abgeschlossen/gespeichert ist und Komponente2 ihn ebenfalls erstellen will. Ich bin mal über "CriticalSection" gestolpert aber mir ist das ehrlich gesagt zu hoch für mein Hirn. War immer so viel Source-Code :)

Kann mir da evtl. jemand helfen? Vielleicht gibt's ja auch noch eine andere Lösung?

himitsu 26. Feb 2020 01:01

AW: TDictionary und TThread
 
TCriticalSection ist ja Windows MSDN-Library durchsuchenEnterCriticalSection, auch wenn Delphi diese Klasse schon für andere Plattformen erweitert hat.

Hier könnte man zwar auch einen MultiReaderSingleWriter verwenden, da sich das Dictionary parallel auslesen ließe, aber wenn nicht soooooo oft gelesen wird, dann lohnt es sich nicht.
Delphi-Referenz durchsuchenTSimpleRWSync bzw. Delphi-Referenz durchsuchenTMultiReadExclusiveWriteSynchronizer/TMREWSync

Dann gibt es noch das "neuere" TMonitor in der System-Unit, welches sich nativ an jedes TObject hängen lässt. (mit dem Namen der nicht mit TMonitor aus Forms zu verwechseln ist)

Es gibt auch eine TThreadList, was eine TList ist und bereits sowas wie eine CriticalSection enthält.
Add, Remove und Clear sind bereits threadsave und ansonsten kann man sich die innere TList auch gesperrt rausholen und längere Aktionen mit der Liste machen, während die anderen Threads warten.

Und im Grunde kommt kommt alles auf's Gleiche drauf raus, du mußt die Liste sperren (mindestens) wenn die Liste bearbeitet wird, bzw. auch für die "kurze" Rechenpause zwischen Nachsehn und das Machen.
Delphi-Quellcode:
TMonitor.Enter(List);
try
  if not List.Find(xyz) then
    List.Add(xyz);
finally
  TMonitor.Exit(List);
end;
Atomare Operationen braucht man nicht selbst absichern, wie z.B. InterlockedIncrement bzw. das plattformunabhängige AtomicIncrement oder das TThreadList.Add,
aber alles Andere muß geschützt werden, wie z.B. im Beispeispiel die Lücke zwischen If, Find und Add, wo ja ein anderer Thread zwischendurch das auch einfügen kann und das Ergebnis vom IF somit schon nicht mehr stimmt, wenn Add ausgeführt wird.

Alle anderen Sperr-Klassen mußt du ja neben dein zu Sperrendes legen, während TMonior direkt an der Klasse hängt, aber mit dem "Nachteil", dass man in der Definition nichts von dieser Sperr-Instanz sieht. :freak:
Hier kann man aber das Verhalten des Compilers ausnutzen, dass er unbekannte Attributklassen einfach ignoriert,
um hier in der Deklaration zu "dokumentieren", dass unten TMonitor verwendet wird.
Delphi-Quellcode:
var
  [Monitor] // oder [Lock]
  List: TDictionary<string,string>;
Delphi-Quellcode:
type
  MonitorAttribute = class(TAttribute);

var
  [Monitor]
  List: TDictionary<string,string>;
PS: Globale Variablen sind eh böse und ich würde das Dictionary als Class Var in die Threadklasse integrieren.
Delphi-Quellcode:
type
  TMyThread = class(TThread)
  public class var
    List: TDictonary<string,string>;
  protected
    procedure Execute; override;
  end;

// von außerhalb
TMyThread.List[abx] := def; //TMyThread.List.Add(abx, def);

MicMic 27. Feb 2020 00:25

AW: TDictionary und TThread
 
Dankeschön für die Hilfe.
Noch habe ich keine Zeit gefunden... ist für mich jetzt auch nicht einfach. Aber ich erstelle mal ein neues Projekt und versuche mich da mal dran.
Aber ich habe mal für mein Projekt eine andere Idee umgesetzt. Wenn Thread 1 läuft, mach ich Pause für Thread 2 (und umgekehrt). So kann für's TDictionary nicht gleichzeitig gleiche Einträge erstellt werden. Und wenn die Abfragen von Werten nicht gerade klappt, werden halt die Daten geladen. Die Sache wird auch fast nur bei Programmstart schwierig. Dafür kann ich noch ein wenig tricksen. Im Programmverlauf werden dann eher selten gleiche Daten für's TDictionary erstellt und die Idee mit dem pausieren eines Threads und somit ab und zu doppeltes laden von Daten hält sich in Grenzen.

himitsu 27. Feb 2020 00:55

AW: TDictionary und TThread
 
Was, du hälst von außen den anderen Thread an fragwürdig undefinierter Stelle an?
Delphi-Referenz durchsuchenTThread.Suspend

Wenn das, dann NIEMALS, schäm dich, steinigt ihn, ...
Nicht umsonst wurden Resume/Suspend inzwischen als deprecated deklariert.

Mit etwas Glück kann man durch sowas sein komlettes Programm lahmlegen,
z.B. wenn der Thread gerade dabei war beim MemoryManager eine Speicheranforderung zu machen (Get/Free/Alloc/Realloc/...) und der Thread gerade da pausiert wird, wo er eine der CriticalSections im Manager blockiert hat und der andere Thread im selben Speicherbereich etwas machen will.

Für eine "einfache"/billige Threadabsicherung ohne eigene Sperrinstanz kann man auch SendMessage oder TThread.Synchronize/Queue benutzen.
Delphi-Quellcode:
procedure ...;
var
  B: Boelean;
begin
  ...
  //Synchronize(procedure // innerhalb einer TThreadklasse
  TThread.Synchronize(nil, procedure
  begin
    B = List.Find(xxx);
  end);
  if B then

Rollo62 27. Feb 2020 06:59

AW: TDictionary und TThread
 
Statt Suspend würde ich da irgendwas mit Schleife, Wait und Event machen.
https://stackoverflow.com/questions/...ce-to-start-up


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