Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Thread sichere Datenabfrage (https://www.delphipraxis.net/205907-thread-sichere-datenabfrage.html)

Kishmet 29. Okt 2020 09:54

Thread sichere Datenabfrage
 
Guten Morgen,

(Falls dieses Thema zweimal auftaucht tut es mir leid, mein erster Versuch Wollte nicht so richtig, kann aber sein das er doch noch durchrutscht):?

Ich arbeite jetzt seit zwei Jahren mit Delphi (derzeit 10.4.1) und bin nun das erstemal an einem Punkt an dem ich nicht recht weiter komme und auch Online finde ich grade entweder nicht die richtigen Schlagworte oder habe einfach Verständniss Probleme...:oops:

Das Drumherum:

Ich habe einen Mainthread der Daten von einem Gerät ausliest und diese sowohl auf dem Monitor darstellt als auch immer hinten an eine Datei anhängt. (eigentlich sind das mehrere threads aber das tut soweit)

Nun habe ich einen zweiten Thread gebastelt, der diese Daten einließt (wirklich nur liest) und auswertet. Dann schreibt er die Ergebnisse in eine andere Datei. Wenn ich in der Messung zurückScrolle sollten dann diese Ereignisse auch vom Main thread ausgelesen und dargestellt werden können. Da die Ergebnisse Variieren lese und werte ich immer alles ein/aus. Das könnte man ändern wäre aber sehr aufwendig und ich bin mir nicht sicher ab das am generellen Problem etwas ändert.

Mein basis Problem:

Manchmal kommt es bei mir zu einer Art Integerüberlauf - ich habe bisher noch nie Zugriffsverletzungen oder derlgeichen bekommen und bin mir nicht sicher woran es liegt.
Delphi-Quellcode:
 
DieKlasse = class
private
  FDataReader : TMyBufferedFileReader;
  FReadDataValueNum: Int64;
  //... und andere
end;

Function DieKlasse.ReadInData(var Abuf: TMyData):Cardinal;
var
  idx: Int64;
begin
  if FReadDataValueNum >= FDataReader.ValueCount then
  begin
    idx := FReadDataValueNum - FDataReader.ValueCount;
    if idx < length(FOnlineWriteCache) then
    begin
      ABuf  := FOnlineWriteCache[idx]; //Hier entsteht der fehler(sehr sporadisch) wenn idx zu groß wird (was ja aber eigentlich gar nicht sein kann)
      result := 1;
    end else begin
      result := 0;
    end;
  end else begin
    FDataReader.ValueNumber := FReadDataValueNum;
    result                 := FDataReader.Read(ABuf, 1);
  end;
end;
1. Ich bin mir immernoch nicht ganz sicher ob einfaches Lesen beim Multithreading ein Problem darstellt?? Ob dabei Müll rauskommt wäre mir an der Stelle auch egal, das Fange ich Ohnehin ab. Aber stellt das Prinzipiell ein Problem dar?

2. Muss ich die Abfragen der Streams in critical Sections packen oder gibt es hier befehle (vorgefertigt) die das Lesen und schreiben bereits Threadsicher machen? Alla: "ReadThisFileAndLockItWhileDoingSo"

3. Wenn ich Critical sections einführe sperren diese dann auch alle Variablen/Dateien usw. die ich innerhalb dieser Section verwende?

Über ein wenig Rat wäre ich super dankbar. Das Thema Threads ist mir einfach immernoch nicht 100% geheuer.

Lieben Gruß
Kishmet

PS: Vielen dank für das Tolle Forum und die Tollen Beiträge vom harten Kern! Ihr seit echt Spitze und ich hab schon soooo viel von euch gelernt!:thumb:

Kishmet 29. Okt 2020 11:08

AW: Thread sichere Datenabfrage
 
hm... also hier mal meine Gedanken zu dem Thema:

wenn FReadDataValueNum >= FkanalDatareader.ValueCount ist, dann kann folglich in der nächsten Zeile idx nicht negativ oder zu groß werden. Außer ein anderer Thread ändert just in dem Moment den ValueCount.... Korrekt? Ist die Einzige logische Erklärung die mir noch einfällt.... Wobei er eigentlich auch nicht zu groß werden kann da ich das ja wiederum abfrage und dann eh raus bin... Noch dazu tritt das nur sporadisch auf, daher ist es so schwer für mich zu kontrollieren was tatsächlich drin steht...

himitsu 29. Okt 2020 11:19

AW: Thread sichere Datenabfrage
 
Überall muß um alles, was auf diese Variablen zugreift, die selbe CriticalSection (oder Ähnliches, z.B. siehe Delphi-Referenz durchsuchenSyncObjs).


Nur Lesend, wenn währenddessen nirgendwo Schreibend zugegriffen werden kann, da kann man es auch ohne Synchronisation machen, aber nur wenn beim Lesezugriff nichts geschrieben wird.
z.B. Stream.Read ist ein schreibender Zugriff, da dort der Posiotionszeiger verändert wird.

Auch Lesend auf eine String-Variable oder Interface-Zeiger zugreifen ist ein schreibender Zugriff,
aber die Referenzzählung ist mit den nachfolgend erwähnten atomaren Operationen gelöst.



Zuweisen/Ändern von Integern/Pointer, da gibt es auch die Möglichkeit von "atomaren" Zugriffen durch die CPU,
siehe MSDN-Library durchsuchenInterlockedAdd und Auslesen geht ohne Extrafunktion (siehe "nur Lesend", da die CPU während des Schreibens alles blockt).

Mehrfach auf den "selben" Wert zugreifen, dann vorher in eine lokale Variable kopieren.
z.B.
Delphi-Quellcode:
if myvar > 0 then MachwasMit(myvar);


nur Windows: Bei Google suchenInterlockedIncrement
ansonsten siehe Delphi-Referenz durchsuchenTInterlocked.Increment bzw. die neuen platformübergreifenden Delphi-Referenz durchsuchenAtomicIncrement
oder LOCK im Assembler. https://www.felixcloutier.com/x86/xadd https://stackoverflow.com/questions/...n-x86-assembly

stahli 29. Okt 2020 11:20

AW: Thread sichere Datenabfrage
 
Herzlich willkommen.

Konkret helfen kann ich nicht, aber falls Du die Kanäle nicht kennst, dann schau Dich mal dort noch um:
https://www.youtube.com/user/EmbarcaderoGermany/videos
https://www.youtube.com/c/Embarcader...ologies/videos

PS: Trage mal noch Deine Delphi-Version in Deinem Profil ein.

Kishmet 29. Okt 2020 12:07

AW: Thread sichere Datenabfrage
 
Zitat:

Überall muß um alles, was auf diese Variablen zugreift, die selbe CriticalSection (oder Ähnliches, z.B. siehe Delphi-Referenz durchsuchenSyncObjs).
Puh Das würde ne mittlere Katastrophe geben. Da der reader an sich abgeleitet ist, Unterfunktionen hat und auf diesen an unzähligen stellen zugegriffen wird. Kann man die critical Section denn auch außerhalb des Threads deklarieren, also bpsw. direkt im Reader der ja universell einsetzbar ist? Ich habe das bisher nur im Create bzw. im Execute des Threads gesehen(in den Beispielen die ich zu dem thema gefunden habe)... Wenn ich die Section aber im Objekt (also im Reader) erzeugen könnte wäre das vermutlich, zumindest gefühlt, einfacher.

Zitat:

Nur Lesend, wenn währenddessen nirgendwo Schreibend zugegriffen werden kann, da kann man es auch ohne Synchronisation machen, aber nur wenn beim Lesezugriff nichts geschrieben wird.
z.B. Stream.Read ist ein schreibender Zugriff, da dort der Posiotionszeiger verändert wird.
Das war mir so tatsächlich noch nicht ganz klar.:shock: Jetzt muss ich tatsächlich sagen das ich überrascht bin das es überhaupt läuft... Vielleicht steckt da auch schon irgendwo ne critical Section die ich noch nicht gefunden habe. Ich mach mich mal auf die Suche.

Zitat:

Konkret helfen kann ich nicht, aber falls Du die Kanäle nicht kennst, dann schau Dich mal dort noch um:
Links zu guten Videos sind immer gold wert. Die hier kenn ich zwar schon, aber ich hab für dieses Problem dort tatsächlich noch nicht gesucht... Danke dir :thumb:

Jetzt nochmal eine Frage von Oben, die ist noch nicht so richtig beantwortet worden glaube ich:

Gibt es eine Lese bzw. eine Schreibfunktion in der von Haus aus eine Lock integriert ist? und erst wenn das Fertig ist, kann ein anderer thread wieder darauf zugreifen?

reaktor 29. Okt 2020 13:52

AW: Thread sichere Datenabfrage
 
Hallo.
Ein mMn sehr guter Beitrag zum Thema Synchronisation von Bernd Ua:
Video:
https://www.youtube.com/watch?v=WOc89TF8l-8
Material:
http://probucon.de/wp-content/upload...ronisation.pdf

Zitat:

Gibt es eine Lese bzw. eine Schreibfunktion in der von Haus aus eine Lock integriert ist? und erst wenn das Fertig ist, kann ein anderer thread wieder darauf zugreifen?
Neben Critical Sections, TMonitor und vielen anderen gibt es noch eine threadsichere Warteschlange: TThreadedQueue.
Beispiel dazu hier:
http://chee-yang.blogspot.com/2015/1...eadsynchronize

Evtl ist auch TMultiReadExclusiveWriteSynchronizer für dich der richtige Weg.
Auszug aus dem o.g. PDF:

TMultiReadExclusiveWriteSynchronizer :
• Verwaltet mehrere lesende Zugriffe und
blockierenden Schreibzugriff
• BeginRead/EndRead zum Lesen
• BeginWrite/EndWrite zum Schreiben
• Zum Beispiel verwendet von der VCL via
IReadWriteSync in Forms (GlobalNamespace)

In dem Buch "Delphi Cookbook" gibt es, glaube ich, ein Beispiel für ein Oszilloskop was auf Multi-Threading basiert. Die Problemstellung klang jetzt auf Anhieb irgendwie ähnlich.

Tut mir leid, dass ich keine konkretere Hilfe anbieten kann.

Kishmet 29. Okt 2020 14:10

AW: Thread sichere Datenabfrage
 
hihi :-D das video hab ich grade auf. Danke dir @Reaktor

Mein größtes Problem ist grade tatsächlich auch das generelle Verständniss, was aber vermutlich einfach daran liegt das der Code in dem ich arbeite schon die eine oder andere zeile hat... und davon auch einfach viel nicht von mir ist, sondern von jemandem den ich deswegen auch nicht mehr fragen kann... :?

Zitat:

Evtl ist auch TMultiReadExclusiveWriteSynchronizer für dich der richtige Weg.
Das hört sich interressant an. Das muss ich mir mal ansehen.

Prinzipiell sollte es auch bei mir irgendwo in der Nähe des Codes zum schreiben und lesen bereits etwas geben was die threadsicherheit garantiert. Denn es geht ja auch das Rückblättern schon, wäre das nicht sicher dürfte auch dieser Teil nicht richtig funzen, ich bin leider bisher nicht dahinter gekommen, wie das genau funktioniert: Da keine critical section, Monitor synchronize oder sonstiges aufgerufen wird, ist das für mich grade nicht ganz plausibel... Das einzige was mir auffällt ist das die Lese und Schreib Prozesse über Pointer verbogen werden o.O . Vielleicht liegt es aber auch daran das die Datei jeweils zwischen gebuffered wird. So richtig Sinn bekomme ich in die Sache nur leider heute iwie nicht rein...

Zitat:

In dem Buch "Delphi Cookbook" gibt es, glaube ich, ein Beispiel für ein Oszilloskop was auf Multi-Threading basiert. Die Problemstellung klang jetzt auf Anhieb irgendwie ähnlich.
Prinzipiell ist es mehr oder minder genau das was ich gerade Bastel. Ein Oszi in dem Zurückgespult werden kann und das gleichzeitig das Signal auswertet. Was mich gerade iritiert ist das es läuft :oops: - Denn himitsu hat recht, das lesen und schreiben sollte sich eigentlich beißen... naja. Ich werde mir das Buch mal holen, verkehrt ist das sicherlich nicht.

himitsu 29. Okt 2020 18:10

AW: Thread sichere Datenabfrage
 
Statt CriticalSection gibt es seit 'ner Weile auch TMonitor.
(Achtung, nicht das TMonitor aus TScreens sondern aus System ... ja, ich weiß der Name ist doof/irrsinnig/verwirrend, aber wenn jemand strunzdoof von C# klaut kopiert, dann kommt sowas bei raus)

Die TMonitor können an ALLE TObjekt angehängt werden.
Besonders geil, weil man so keine Extra-Variable braucht und es sogar ohne Umbauten an Fremdkomponenten nutzen kann (z.B. an einer TStringList).

Delphi-Quellcode:
TMonitor.Enter(MyList); // siehe auch TryEnter/Wait/Pulse und der Timeout im Parameter
try

finally
  TMonitor.Exit(MyList);
end;
Statt TMonitor.Enter geht auch MonitorEnter

und könnte man sich auch selbst via ClassHelper als Methoden an TObject/TMyList/... hängen.
Delphi-Quellcode:
MyList.Enter; // oder z.B. Lock und Unlock
try

finally
  MyList.Exit;
end;

reaktor 30. Okt 2020 10:13

AW: Thread sichere Datenabfrage
 
Ist dein Programm reinzufällig 32 bit?

Wenn ja und der von dir angegebene Code einigermaßen der Realität entspricht und die Threads so laufen, wie ich es denke, hätte ich eine mögliche Erklärung.
zu finden unter:
https://hub.packtpub.com/common-prob...l-programming/

Dort werden ein paar (nicht gleich so eindeutige) Fallstricke bei geteilten Ressourcen erklärt.
Ein Beispiel geht um die Problematik mit 64 bit Datentypen (int64) wenn der Processor im 32 bit Modus arbeitet. Um die Variable auszulesen braucht der Prozessor zwei Schritte. Und zwischen diesen beide Schritten kann der Writer-Thread den noch nicht gelesenen Teil überschreiben. Je nachdem was der Writer-Thread nun manipuliert, könnte es FReadDataValueNum oder FDataReader.ValueCount sein. Dadurch kommt für idx ein nicht plausibler Wert raus und dann scheitert auch der Zugriff das Array oder die Liste. Das könnte auch das sporadische Auftreten erklären.
Selbst wenn das jetzt nicht die Ursache ist, fand ich den Beitrag irgendwie spannend. Das Buch kann ich auch empfehlen, falls man das hier überhaupt darf.

Zitat:

Mein größtes Problem ist grade tatsächlich auch das generelle Verständniss
Hab mich auch erst kürzlich damit beschäftigt. Wenn mir Sachen einfallen die mir geholfen haben, poste ich sie.


Zitat:

Prinzipiell sollte es auch bei mir irgendwo in der Nähe des Codes zum schreiben und lesen bereits etwas geben was die threadsicherheit garantiert. Denn es geht ja auch das Rückblättern schon
Kann auch reiner Zufall sein (?). Gerade das Fehler finden fand ich bei Threads ziemlich eklig. In solchen Fällen habe ich mir oft mit Debugstrings geholfen (OutputDebugString ist thread-sicher, das Auslesen des Int64-Wertes wie gesagt unter 32 bit aber nicht).


Auf jeden Fall musst du dich um die Synchronisation kümmern.
Such am besten erstmal ob das nicht schon irgendwo passiert. Soweit ich das verstanden habe, kannst du eine Critical section relativ weit "außen" setzen, also z.B. gleich bei der ersten Funktion, die dann weitere Funktionen aufruft (Ich finde leider die Quelle dazu nicht mehr. Bitte korrigiert mich, wenn das nicht stimmt). Gut möglich, dass dein ehemaliger Kollege die Synchronisation auf einer anderen Ebene eingebaut hat.
Der, durch CS geschützte Code, sollte allerdings wegen Deadlocks und Performance so kurz wie möglich bleiben. Und es müssen natürlich alle Threads auf die CS eingehen, nicht nur einer.

Ist keine Synchronisierung vorhanden, musst du diese (unbedingt!) einbauen. Aber an welchen Stellen und mit welchen Mitteln, kann ich leider von außen nicht beurteilen. Halt immer dort wo es vorkommen kann, dass zwei oder mehrere Threads auf eine geteilte Ressource zugreifen. Gut möglich, dass es an den Pointern oder den Dateien liegt. Evtl auch das Bit-Problem oder einfach nur ein ungeschützer Zugriff auf Variablen oder Klassen.

Hier noch ein kleiner Link zu CS:
http://www.delphicorner.f9.co.uk/articles/op4.htm

und hier noch einer zu simplen Datentypen und deren Threadsicherheit:
https://stackoverflow.com/questions/...es-thread-safe

Viel Erfolg!

Kishmet 30. Okt 2020 10:46

AW: Thread sichere Datenabfrage
 
Zitat:

Ist dein Programm reinzufällig 32 bit?
Ja, ...

Das werde ich mir Montag mal in Ruhe ansehen. Das könnte durchaus so sein. Das würde mich allerdings dezent verunsichern, denn ich nehme Codeelemente die mehr oder minder genauso auch an anderen Stellen für ähnliche Zwecke verwendet werden ... Ich Tippe aber drauf das ich wirklich noch irgendeinen Mechanismus zum Sperren einfach immernoch nicht gefunden habe, der in irgendeiner Hintertür eingebaut ist. Die Reader und Writer Elemente sind einfach extrem verschachtelt.

Die Basics und auch wie ich eine CS verbaue habe ich drauf. Ich denke das ich auch mit dem TMonitor gut zurecht kommen würde (bisher nie verwendet). Mit amphoren und so weiter habe ich gottseidank noch keine Berührungspunkte, das wird aber vermutlich in ein oder zwei Jahren dann unumgänglich :shock: . Mal sehen wie sich das Projekt weiterentwickelt.

Über das Wochenende werde ich einfach mal einen Test mit vielen Daten starten. Wenn es mir bis Montag um die ohren fliegt weiß ich wenigstens sicher das es ein Problem gibt. Sonst muss ich weiter suchen bis ich die Lösung gefunden habe.

Ich danke euch für den großartigen Input! Ich mach mich mal auf die Suche und melde mich sobald ich was herausfinde!


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