Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Viele Threads untereinander synchronisieren (https://www.delphipraxis.net/146150-viele-threads-untereinander-synchronisieren.html)

argonix 14. Jan 2010 15:25


Viele Threads untereinander synchronisieren
 
Hallo!
Ich habe folgendes (merkwürdige) Problem:
In einem Thread werden Werte an andere Threads vergeben. Diese Threads werden vom Mainthread ausgeführt. Der Thread, der Werte an die anderen Threads senden soll, hat eine Liste von Objekten, welche eine Referenz zu den anderen Threads halten.
Wenn ich also dieses Signal an alle Threads in der Liste senden will, müsste ich prinzipiell sowas machen:
Delphi-Quellcode:
procedure TMyThread.EmitSignal;
var i: Integer;
begin
 for i:=0 to objlist.Count-1 do
  objlist[i].OwnedThread.ReceiveSignal();
end;
Dabei ist zu beachten, dass die Objekte in der Liste dem Thread gehören, auf den mit "OwnedThread" zugegriffen wird.
So. Und jetzt soll die Funktion oben aber nicht so aufgerufen werden, sondern ordentlich mit dem Thread Synchronisiert werden.
Denkbar wäre sowas:
Delphi-Quellcode:
procedure TMyThread.EmitSignal();
begin
 Synchronize(objlist[?].OwnedThread,@SyncSignal);
end;

procedure TMyThread.SyncSignal;
begin
 objlist[?].OwnedThread.ReceiveSignal();
end;
Und hier sieht man das Problem: Wie hole ich den korrekten Thread aus der Liste, und synchronisiere die Funktionen richtig? Dummerweise akzeptiert TThread.Synchronize ja nur Methoden vom Typ TThreadMethod und keine lokalen.
Weiß jemand da eine gute Lösung für?

Blup 15. Jan 2010 12:19

Re: Viele Threads untereinander synchronisieren
 
Synchronize dient dazu, Methoden im Kontext des Hauptthreads auszuführen.
Mir ist keine Methode bekannt, die so etwas für Threads untereinander implementiert.

Es bietet sich an, die Methode ReceiveSignal jeweils threadsicher zu programmieren (z.B. mit kritischen Abschnitten).

argonix 15. Jan 2010 12:25

Re: Viele Threads untereinander synchronisieren
 
Und wofür genau ist dann
Delphi-Quellcode:
class procedure Synchronize(AThread: TThread; AMethod: TThreadMethod);
?
Kritische Abschnitte - wie funktioniert das genau mit Delphi? Gibt es da eine Anleitung zu?

Blup 15. Jan 2010 13:22

Re: Viele Threads untereinander synchronisieren
 
Zitat:

Zitat von argonix
Und wofür genau ist dann
Delphi-Quellcode:
class procedure Synchronize(AThread: TThread; AMethod: TThreadMethod);
?

Zitat:

Zitat von Blup
Synchronize dient dazu, Methoden im Kontext des Hauptthreads auszuführen.

Zitat:

Zitat von argonix
Kritische Abschnitte - wie funktioniert das genau mit Delphi? Gibt es da eine Anleitung zu?

In Delphi werden kritische Abschnitte mit TCriticalSection oder TMultiReadExclusiveWriteSynchronizer gekapselt.
Die Hilfe ist dazu leider ziemlich kurz.

Im Forum wirst du aber einige Beiträge finden, z.B. diese:
http://www.delphipraxis.net/internal...t.php?t=167800
http://www.delphipraxis.net/internal...t.php?t=155226

stoxx 15. Jan 2010 13:59

Re: Viele Threads untereinander synchronisieren
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hier ein paar einfache Beispiele, um sich die Grundlagen für CriticalSections, TMultiReadExclusiveWriteSynchronizer und dem Gebrauch eines Mutex
anzueignen ... ein Thread liest Daten, der andere Thread schreibt Daten.
Das muss synchronisiert werden
Am besten mit dem Beispiel ThreadsNoSync anfangen, damit man sieht, wie es nicht geht und was passieren kann, wenn man die Synchronisation nicht beachtet.

argonix 15. Jan 2010 14:14

Re: Viele Threads untereinander synchronisieren
 
Zitat:

Zitat von Blup
Zitat:

Zitat von argonix
Und wofür genau ist dann
Delphi-Quellcode:
class procedure Synchronize(AThread: TThread; AMethod: TThreadMethod);
?

Zitat:

Zitat von Blup
Synchronize dient dazu, Methoden im Kontext des Hauptthreads auszuführen.


Ich meine: Warum gibt es dann zwei Arten von Synchronize? Eine mit einem Thread als Parameter und eine ohne?
Ich lese mir das mal durch... Eine "einfachere" Möglichkeit scheint es nicht zu geben, oder?

stoxx 15. Jan 2010 14:21

Re: Viele Threads untereinander synchronisieren
 
Zitat:

Zitat von argonix
Ich meine: Warum gibt es dann zwei Arten von Synchronize? Eine mit einem Thread als Parameter und eine ohne?
Ich lese mir das mal durch... Eine "einfachere" Möglichkeit scheint es nicht zu geben, oder?

die Möglichkeiten in den Beispielen sind doch einfach ...

argonix 15. Jan 2010 14:51

Re: Viele Threads untereinander synchronisieren
 
Ja, die probiere ich grade auch aus.
Muss das TCriticalSection-Objekt eine globale variable sein? Oder kann jeder Thread eine eigene Instanz verwenden?

stoxx 15. Jan 2010 15:00

Re: Viele Threads untereinander synchronisieren
 
Zitat:

Zitat von argonix
Ja, die probiere ich grade auch aus.
Muss das TCriticalSection-Objekt eine globale variable sein? Oder kann jeder Thread eine eigene Instanz verwenden?

kommt drauf an, wer wo schreibt.

Alle schreibenden Funktionen auf irgendwelche Daten und alle Lesenden Funktionen müssen syncrhonisiert werden.
Auch wenn es nur ein "kleiner" Integer Wert ist.
Weil es passieren kann, dass halbe Daten, und damit ungültige Daten gelesen und geschrieben werden.
Kleine Ausnahme .. booleans kann man auch so schreiben. Die werden mit einem Prozessorbefehl gelesen, da kann man nix falsch machen.

Wenn Du also ein "Datenobject" hast .. was alle schreib und leseaufträge empfängt, dann muss nur dieses eine Object die Daten syncrhonizieren ..
viele Threads dürfen dann dort reinschreiben und müssen selbst gar nix beachten mit der Synchronization, wenn sie über Write und Read Properties, Funktionen Daten dort reinschreiben ..

.. ich hätte Dir gern noch ein schönes Beispiel von mir geschickt, bin mir aber nicht sicher ob das das richtige war.
Wir hatten nämlich auch ein Beispiel, was auf einer 8 Core XEON Cpu versagt hat.
Weiß jetzt nicht, welches das war in dem Ordner.. hmmm


.. nochmal editiert..

argonix 15. Jan 2010 15:14

Re: Viele Threads untereinander synchronisieren
 
Mein problem ist ja eigentlich folgendes: Ich entwickle eine wissenschaftliche Anwendung, welche dummerweise diese ganzen Threads braucht. (es geht um eine Biosimulation)
Allerdings funktioniert das zusammenspiel manchmal, aber maistens empfängt die Anwendung ein SIGFPE- oder SIGSEGV-Signal (ich nutze Linux). Der Debugger kann die Fehlerstelle nicht finden und die Assemblerdaten sind wertlos.
Also will ich erstmal allen Threads beibringen, ihre Daten "richtig" auszutauschen.

Nochmal konkreter: Ich habe zwei Threads und ein "Zwischenobjekt". Beide Threads machen Berechnungen im Mainloop. Thread2 hält das Zwischenobjekt, Thread1 hat eine Referenz drauf. Thread1 sendet nun Informationen an Thread2, indem eine Funktion im Zwischenobjekt aufgerufen wird, welche die Daten kurz ändert und dann an Thread2 weitergibt.
Dies kann immer passieren, auch wenn Thread2 Suspended ist, Ist Thread2 Suspended und empfängt grade ein Signal, so wird Resume() ausgeführt.

Reicht es also, die CriticalSections im Zwischenobjekt bei der Übertragung auf den Thread2 anzubringen, oder müssen die vorher schon sein, da ja Daten von Thread1 in einem Objekt verändert werden, was Thread2 gehört? (veränderung erfolgt durch Aufruf der Methode des Zwischenobjektes)

stoxx 15. Jan 2010 15:31

Re: Viele Threads untereinander synchronisieren
 
also .. das Problem bei Threads ist eigentlich nur, dass ein Thread Daten schreibt, noch gar nicht fertig ist, und ein anderer Thread diese Daten schon liest, und dann "halb" richtige Daten liest.
Stell Dir einen Integer vor, der hat 32 Bit. also 4 Bytes.
Ein Thread fängt jetzt an, diesen Integer von 4 Byte zu schreiben und ist gerade fertig geworden mit einem Byte.
(Auch wenn es nur ein Befehl in Delphi ist, sind es unter umständen mehrere Befehle in Assembler für den Prozessor)

Wenn jetzt gerade in dem Moment ein anderer die Daten liest, dann liest er 4 Byte.
Ein Byte ist schon aktuell, die anderen 3 Byte sind noch irgendwelcher Quatsch, der vorher drin war.
Jetzt liest er einen völlig anderen Integer, als gewollt.

Es soll Quelltext geben, die mit ungültigen Daten schlecht klar kommen und dann Exceptions verursachen :-)


Deswegen darf das Schreiben von Daten immer nur hintereinander geschehen. Und von einem Thread !
Da es verschwendung wäre, Daten nur hintereinander zu lesen, dürfen also beim Lesen auch beliebig viele Threads auf Daten zugreifen.
Es muss nur sicher gestellt werden, dass gerade niemand schreibt in dieser Zeit.
Deswegen gibt es auch das lustige Object, dass früher mal Borland für Dich programmiert hat, mit dem lustigen Namen

"TMultiReadExclusiveWriteSynchronizer"

Eine kleine Ausnahme sind Datentypen, die nur ein Byte benötigen. das Macht der Prozessor in einem Rutsch, da kann auch niemand dazuwischenfunken ... das muss nicht syncrhonisiert werden... der Prozessor liest Byteweise, nicht Bitweise ..

Zitat:

Reicht es also, die CriticalSections im Zwischenobjekt bei der Übertragung auf den Thread2 anzubringen, oder müssen die vorher schon sein, da ja Daten von Thread1 in einem Objekt verändert werden, was Thread2 gehört? (veränderung erfolgt durch Aufruf der Methode des Zwischenobjektes)

kannst Du denn etwas Code posten?
wo liegen jetzt die Daten, auf die beide Threads gleichzeitig schreiben und lesen könnten?
Geschieht diese über "zentrale" Schreib und Lesefunktionen.
Wenn Du natürlich Globale Daten hast, auf die beide Threads zugreifen (auch den Hauptthread nicht vergessen, das ist auch einer)
dann musst Du auch ein globales Syncobject einführen ...


Wenn Du das lesen und schreiben allerdings an zentraler Stelle machst und alle Threads nur diese Funktionen aufrufen, dann nur dort .

argonix 15. Jan 2010 15:48

Re: Viele Threads untereinander synchronisieren
 
Okay... Also das Zwischenobjekt ist in etwas so definiert:
Delphi-Quellcode:
TConnection = class
 private
  owner: TWThread;
  weight: Double;
 public
  constructor Create(aOwner: TWThread;aConn: TWThread);

  procedure ProcessSignal();
end;
procedure TConnection.ProcessSignal;
begin
 owner.ReceiveSignal;
 Inc(weight);
end;
Die Threads sehen in etwa so aus:
Delphi-Quellcode:
TWThread = class(TThread)
private
 value: Integer;
public
 constrctor Create;
 
 procedure CreateConnection(target: TWThread);
 function ReceiveConnRequest(link: TWThread): TConnection;

 procedure ReceiveSignal;
 procedure Execute();override;
end;

procedure TWThread.CreateConnection(target: TWThread);
begin
 connectionList.Add(target.ReceiveConnRequest(self));
end;

function TWThread.ReceiveConnRequest(link: TWThread): TConnection;
begin
 Result:=TConnection.create(self,link);
 ownedConns.Add(Result);
end;

procedure TWThread.Execute();
begin
 while not Terminated do
 begin
  if berechnungen_moeglich then
  begin
   rechnen;
   irgendwann do
    connectionList[item].ProcessSignal;
  end else Suspend;
end;

procedure TWThread.ReceiveSignal;
begin
 Inc(value);
 if Suspended then Resume;
end;
Zitat:

Zitat von stoxx
wo liegen jetzt die Daten, auf die beide Threads gleichzeitig schreiben und lesen könnten?
Geschieht diese über "zentrale" Schreib und Lesefunktionen.

Die Daten liegen im Verbindungsobjekt, welches selbst einem Thread gehört. Dieses Objekt verschickt auch Daten an den Thread, dem es gehört. Lesender Zugriff wird unter den Threads nicht gemacht, aber von anderen Objekten der GUI.

Ist vielleicht jetzt etwas klarer...

stoxx 15. Jan 2010 15:57

Re: Viele Threads untereinander synchronisieren
 
Zitat:

Ist vielleicht jetzt etwas klarer...
eher nicht, weil nicht so recht ersichtlich ist,wo jetzt die gemeinsam genutzten Daten liegen.
und wann wie wo wer was nutzt.

es gibt auch eine Threadsichere TList, namens "TThreadList ", wenn es sich bei den "gemeinsam genutzten Daten um eine TList handelt. da kann man sich critical Sektions sparen und die Liste "locken" und "unlocken"
hab ich aber noch nie verwendet ..

hab meine erklärung nochmal bissl verändert. Eigentlich müsste das weitere Vorgehen damit jetzt klar sein, nur Du kennst Dein Anwendung ...


Zitat:

Lesender Zugriff wird unter den Threads nicht gemacht, aber von anderen Objekten der GUI.
die GUI ist der Hauptthread, und damit auch ein Thread ....
somit ergibt sich ein neues Problem .. die VCL ist nicht threadsicher ..

das heißt .. Kommunikation mit der GUI nur über die nicht ganz optimale Synchronize Funktion .....
da bleibt Dir leider nix anderes übrig ..

argonix 15. Jan 2010 16:02

Re: Viele Threads untereinander synchronisieren
 
Zitat:

Zitat von stoxx
Zitat:

Ist vielleicht jetzt etwas klarer...
eher nicht, weil nicht so recht ersichtlich ist,wo jetzt die gemeinsam genutzten Daten liegen.
und wann wie wo wer was nutzt.

Es wird nichts gemeinsam genutzt. Es wird nur ein Signal von einem zum anderen Thread gesendet, einen Zahlenwert um X einheiten zu erhöhen.
Wann wo wie welcher Thread daten nutzt ist aber wirklich unmöglich vorherzusagen. Es hängt von einem Threadinternen wert ab, der in Execute() bearbeitet wird, ob Thread1 ein Signal über das Linkobjekt an Thread2 sendet.

Wie benutze ich das TMultiReadExclusiveWriteSynchronizer? (wer hat sich den diesen sperrigen Namen ausgedacht?)

stoxx 15. Jan 2010 16:08

Re: Viele Threads untereinander synchronisieren
 
isch mach Dir mal ein Beispiel.

Zitat:

Wann wo wie welcher Thread daten nutzt ist aber wirklich unmöglich vorherzusagen.
oh doch, das solltest bei Threads Du sehr genau wissen .... sons wird das mit Threads nix ...

aber warte mal 5 Minuten ...

stoxx 15. Jan 2010 16:12

Re: Viele Threads untereinander synchronisieren
 
Delphi-Quellcode:
Wie benutze ich das TMultiReadExclusiveWriteSynchronizer? (wer hat sich den diesen sperrigen Namen ausgedacht?)
Der TMultiReadExclusiveWriteSynchronizer macht nur bei großen Datenmengen Sinn, bei einzelnen Integern ist der etwas Oversized ...

Die Klasse erlaubt das gleichzeitige Lesen von Daten von mehreren Threads, sofern gerade kein Thread schreibt, stellt aber sicher, dass wenn geschrieben wird, alle anderen schreibenden und lesenden Threads blockiert werden ...

argonix 15. Jan 2010 16:26

Re: Viele Threads untereinander synchronisieren
 
Wow! Ich habe jetzt mal die entsprechenden Stellen mit CriticalSections geschützt und - voilà, seit 2 Minuten keine Fehler mehr!
Hoffentlich bleibt das so... Aber schonmal vielen Dank! :thumb: Sieht bis jetzt super aus!

EDIT: Zu früh gefreut... Ich bekomme wieder SIGFPEs ohne Backtrace... Der Fehler kann aber überall anders auch liegen. Ich schaue mir das nochmal genauer an.

stoxx 15. Jan 2010 16:27

Re: Viele Threads untereinander synchronisieren
 
Zitat:

Wann wo wie welcher Thread daten nutzt ist aber wirklich unmöglich vorherzusagen. Es hängt von einem Threadinternen wert ab, der in Execute() bearbeitet wird, ob Thread1 ein Signal über das Linkobjekt an Thread2 sendet.
seine eigenen Daten darf natürlich jeder Thread nutzen wie er will ...
zu beachten ist allerdings, dass nur die Execute Methode im Thread läuft.
alle anderen Funktionen laufen im Hauptthread .. es sei denn , sie werden von Execute Aufgerufen.
Datenzugriffe in einer THread Klasse können also dennoch von unterschiedlichen Threads aufgerufen werden ...
Was zu beachten wäre ...


Zitat:

EDIT: Wow! Ich habe jetzt mal die entsprechenden Stellen mit CriticalSections geschützt und - voilà, schon seit 4 Minuten keine Fehler mehr! Hoffentlich bleibt das so... Aber schonmal vielen Dank! Sieht bis jetzt super aus! ?)
na das ist doch schön :-)

argonix 16. Jan 2010 12:37

Re: Viele Threads untereinander synchronisieren
 
So, jetzt läuft wirlich alles! Die verbelibenden Fehler hatte alle mit der grafikbibliothek Qt4 zu tun und sind jetzt behoben. Ein "CriticalSection" in den Zeichenfunktionen hat sich dabei als sehr sinnvoll herausgestellt, obwohl da nur gelesen wird. (Aber wenn man versucht, was zu lesen was nicht existiert ist das halt ein Problem...)
Nochmals vielen Dank! :bounce1:


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:04 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz