Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Thread will nicht synchron schwimmen? (https://www.delphipraxis.net/164110-thread-will-nicht-synchron-schwimmen.html)

Edlmann 28. Okt 2011 18:29

Thread will nicht synchron schwimmen?
 
Hey DP,

Ich versuche grad zum xten Mal, mein Projekt Shapewars auf eine Multithread-anwendung umzustellen. Hierfür möchte ich zunächst die Partikeleffekte und deren Berechnung in einen Thread auslagern. Hierfür hab ich mir einen eigenen TThread abgeleitet, im Moment mit folgendem Code:

Delphi-Quellcode:
type
  TWorkerthread = class(TThread)
  private
  protected
    procedure Execute; override;
  public
    procedure SetJob(Val: Boolean);
    function GetJob: Boolean;
  end;

var
  Fin, HasJob: Boolean;



implementation

uses Main;

function TWorkerthread.GetJob: Boolean;
begin
  NotifText := BoolToSTr(Fin, true);
  NotifTime := 100;
  Result := not Fin;
end;

procedure TWorkerthread.SetJob(Val: Boolean);
begin
  HasJob := Val;
end;

procedure TWorkerthread.Execute;
begin
  while True do
  begin
  if HasJob then
  begin
  Fin := False;
  // Wenn Partikel eingeschaltet sind
  if RenderPartikel then
  begin
  MovePartikel;
  end;
  HasJob := false;
  Fin := true;
  end
  else
  Sleep(1);
  end;
end;
Die Variable Fin hab ich erstmal nur eingeführt, um zu schauen ob er so macht was er soll. Der Thread ist also eine Endlosschleife, die immer wieder nachschaut ob sie die Partikel wieder um einen Schritt weiterbewegen soll (Geschiet mittels MovePartikel) oder eben nicht. Jeden Frame soll also optimalerweise einmal ein Job ausgeführt werden. Nun möchte ich in der Hauptschleife des Spiels (Die über den VCL-Thread läuft) auf die Fertigstellung des Jobs warten, und zwar mittels

Delphi-Quellcode:
while AWorker.GetJob do
  begin
  sleep(1);
  Application.ProcessMessages;
  end;
Wobei AWorker mein Thread ist. Allerdings erscheint so die Form gar nicht mehr, ich muss das Spiel über den TaskManager killen, und lass ich es erst laufen bleibt die Prozessorauslastung bei 0 (also warten beide Thread gerade...), doch anscheinend kommt er aus der Schleife nicht vernünftig wieder raus -.-"

Ich hoffe mir kann jemand weiterhelfen, bin auch nach mehreren Tutorials zu Threads auf keine Lösung gestoßen,
Lg,
Edlmann

Sir Rufo 28. Okt 2011 19:14

AW: Thread will nicht synchron schwimmen?
 
1. Das Verwenden von globalen Variablen ist fast immer falsch
2. Der Austausch von Thread-Status-informationen über eine globale Variable ... geht gar nicht
3. Eigenschaften in einem Thread einfach so zu ändern ein NoGo
4. Einen Thread in einer Endlosschleife zu parken ... besser so, dann kann man den auch mal vernünftig beenden
Delphi-Quellcode:
procedure MyThread.Execute;
begin
  while not Terminated do
    begin
      ...
    end;
end;
Schau dir mal hier im Forum die Informationen zum Thema Hier im Forum suchenThread und Hier im Forum suchenCriticalSection an.

Edlmann 29. Okt 2011 11:55

AW: Thread will nicht synchron schwimmen?
 
Zitat:

Zitat von Sir Rufo (Beitrag 1133266)
1. Das Verwenden von globalen Variablen ist fast immer falsch
2. Der Austausch von Thread-Status-informationen über eine globale Variable ... geht gar nicht
3. Eigenschaften in einem Thread einfach so zu ändern ein NoGo
4. Einen Thread in einer Endlosschleife zu parken ... besser so, dann kann man den auch mal vernünftig beenden
Delphi-Quellcode:
procedure MyThread.Execute;
begin
  while not Terminated do
    begin
      ...
    end;
end;
Schau dir mal hier im Forum die Informationen zum Thema Hier im Forum suchenThread und Hier im Forum suchenCriticalSection an.

Vielen Dank schonmal, damit ist wenigstens der Stil schonmal besser ;)
Das mit den globalen Variablen und dem not Terminated ist mir auch direkt nach dem Posten aufgefallen, ist schon geändert. Funktioniert allerdings noch immer nicht.

Was genau meinst du mit Punkt 3?

grl 29. Okt 2011 15:41

AW: Thread will nicht synchron schwimmen?
 
Edlmann,

also, mal abgesehen von dem was Sir Rufo schon angeführt und der Tatsache, daß du keinerlei Synchronisation hast (Stichwort CriticalSection), sollte das schon irgendwie hinhauen.

Um da einen Fehler zu finden musst du uns schon mit vollständigem Sourcecode füttern.

Mach mal was wirklich kompilierbares (was z.B. ist das NotifText und NotifTime, wozu sind die gut?) und wo ist Renderpartikel und Movepartikel definiert? Sind das lauter Thread-Safe implementierte Dinge?

Ich stell jetzt einfach mal eine Vermutung in den Raum: in Renderpartikel oder Movepartikel machst du einen VCL-Zugriff und das kracht...

Gruß
Luggi

Furtbichler 29. Okt 2011 19:57

AW: Thread will nicht synchron schwimmen?
 
Lustig, wenn man 'Workerthread' in die Suchfunktion eingibt, findet man sogar was.

Edlmann 29. Okt 2011 21:42

AW: Thread will nicht synchron schwimmen?
 
Zitat:

Zitat von grl (Beitrag 1133405)
Edlmann,

also, mal abgesehen von dem was Sir Rufo schon angeführt und der Tatsache, daß du keinerlei Synchronisation hast (Stichwort CriticalSection), sollte das schon irgendwie hinhauen.

Um da einen Fehler zu finden musst du uns schon mit vollständigem Sourcecode füttern.

Mach mal was wirklich kompilierbares (was z.B. ist das NotifText und NotifTime, wozu sind die gut?) und wo ist Renderpartikel und Movepartikel definiert? Sind das lauter Thread-Safe implementierte Dinge?

Ich stell jetzt einfach mal eine Vermutung in den Raum: in Renderpartikel oder Movepartikel machst du einen VCL-Zugriff und das kracht...

Gruß
Luggi

RenderPartikel => Boolean der bestimmt ob Partikeleffekte Gerendert werden sollen
MovePartikel => Arbeitet eine TList mit Partikeln durch, auf die der VCL-Thread nicht zugreift. Somit ist eine CriticalSection nicht nötig.
Ich versteh auch nicht wieso es nicht funktioniert, seit ich die beiden Booleans Fin und HasJob in die klasse des Threads verlagert hab kommen nur AV's bei jedem Aufruf...werd ich mir Morgen in wachem Zustand alles nochmal zu Gemüt führen und nach dem Fehler suchen...das ist das erste Mal das ich Threads benutze, doch eigentlich dachte ich ich hätte genug Tutorials durch um das ganze einigermaßen zu verstehen...mal wieder Fehlanzeigen :D

Zitat:

Zitat von Furtbichler (Beitrag 1133462)
Lustig, wenn man 'Workerthread' in die Suchfunktion eingibt, findet man sogar was.

Ja, darauf bin ich auch gestoßen. Ist aber ziemlicher Overkill für meine Anwendung.

grl 29. Okt 2011 23:17

AW: Thread will nicht synchron schwimmen?
 
Edlmann,

ich kanns nur nochmal sagen - stell ein compilierbares, vollständiges Beispiel ein, bei dem man den Effekt sehen kann. Dann können wir dir sicher helfen.

Nachdem meine Kristallkugel im Wochenende ist, ist es sonst extrem schwierig.

Gruß
Luggi

BUG 30. Okt 2011 00:59

AW: Thread will nicht synchron schwimmen?
 
Hi,

erst möchte ich dir mal etwas Lesestoff geben: http://wiki.delphigl.com/index.php/S...mlung#Partikel

Dann zu deinem Threadproblem: Bei einem Partikelsystem fallen imho einige Beschränkungen weg, die bei normalen Aufgaben bestehen. Es sollte reichen sicherzustellen das nur ein Thread in ein Partikel schreibt. Das kannst du machen indem du zB jedem Partikelbuffer einem Thread zuteilst und die neuertstellten Partikel auf die Buffer verteilst (das musst du dann synchronisieren, wenn du aber im Thread keine Partikel erstellst, sollte zB das suchen toter Partikel auch asynchron möglich sein). Da es aber nicht schlimm ist, wenn mal ein Partikel ein paar Pixel daneben gezeichnet wird, musst du das Auslesen der Buffer zum Zeichnen nicht synchronisieren.

Die einzige Schwierigkeit sehe ich darin die Zeiten zu synchronisieren, da Partikel aber nur Grafik sind muss auch das nicht perfekt sein.

Furtbichler 30. Okt 2011 09:18

AW: Thread will nicht synchron schwimmen?
 
Zitat:

Zitat von Edlmann (Beitrag 1133476)
Zitat:

Zitat von Furtbichler (Beitrag 1133462)
Lustig, wenn man 'Workerthread' in die Suchfunktion eingibt, findet man sogar was.

Ja, darauf bin ich auch gestoßen. Ist aber ziemlicher Overkill für meine Anwendung.

Durch Studium des Overkills hättest Du Einiges an Problemen bei deiner Implementierung vermieden.

Edlmann 30. Okt 2011 14:25

AW: Thread will nicht synchron schwimmen?
 
Zitat:

Zitat von grl (Beitrag 1133487)
Edlmann,

ich kanns nur nochmal sagen - stell ein compilierbares, vollständiges Beispiel ein, bei dem man den Effekt sehen kann. Dann können wir dir sicher helfen.

Nachdem meine Kristallkugel im Wochenende ist, ist es sonst extrem schwierig.

Gruß
Luggi

Wird schwierig, ich werds aber mal versuchen, da was kompilierbares auf die Beine zu stellen.

Zitat:

Zitat von BUG (Beitrag 1133492)
Hi,

erst möchte ich dir mal etwas Lesestoff geben: http://wiki.delphigl.com/index.php/S...mlung#Partikel

Dann zu deinem Threadproblem: Bei einem Partikelsystem fallen imho einige Beschränkungen weg, die bei normalen Aufgaben bestehen. Es sollte reichen sicherzustellen das nur ein Thread in ein Partikel schreibt. Das kannst du machen indem du zB jedem Partikelbuffer einem Thread zuteilst und die neuertstellten Partikel auf die Buffer verteilst (das musst du dann synchronisieren, wenn du aber im Thread keine Partikel erstellst, sollte zB das suchen toter Partikel auch asynchron möglich sein). Da es aber nicht schlimm ist, wenn mal ein Partikel ein paar Pixel daneben gezeichnet wird, musst du das Auslesen der Buffer zum Zeichnen nicht synchronisieren.

Die einzige Schwierigkeit sehe ich darin die Zeiten zu synchronisieren, da Partikel aber nur Grafik sind muss auch das nicht perfekt sein.

Hab ich mir schon durchgelesen, das GPU-System ist mir noch deutlich zu hoch, und das für ältere Hardware hilft mir beim meinem Problem nicht weiter, so ähnlich arbeitet mein System schon ;)

Zitat:

Zitat von Furtbichler (Beitrag 1133504)
Durch Studium des Overkills hättest Du Einiges an Problemen bei deiner Implementierung vermieden.

Nein, nicht wirklich. Ich habe die Basis-Struktur von Threads schon verstanden und auch des öfteren schon benutzt (z.B. bei nem Downloader), bekomme nur die Synchronisation nicht vernünftig hin...

Edlmann 30. Okt 2011 14:40

AW: Thread will nicht synchron schwimmen?
 
Hab jetzt das Threading in eine externe Anwendung übertragen, die jetzt nur das Partikelsystem und den Thread enthält...läuft allerdings selbst in dieser Umgebung nicht so wie ich das will...
Download: http://dl.dropbox.com/u/12342503/ParticleSystem.rar

grl 30. Okt 2011 15:12

AW: Thread will nicht synchron schwimmen?
 
Also, da ist einiges ziemlich schräg... *grins*

Fangen wir mal beim Thread selber an:
Delphi-Quellcode:
procedure TWorkerthread.Execute;
begin
  while True do //<- wie schon mal gesagt: sollte while not Terminated do heissen...
  begin
  if HasJob then //<- nur als Anmerkung: mit ein paar einrückungen würde es leichter zum lesen gehen...
  begin
  Fin := False;
  //Die eigentliche Berechnung
  TParticleSystem(PSystem).Update; //<- und wo initialisierst du PSystem? 
                                   //das muss ja auf ein existierendes TParticleSystem
                                   //verweisen, das auch mit .Create angelegt wurde!
                                   //außerdem ist dein ASystem ja teil des Main-Threads,
                                   //daher muss hier auf eine Synchronisation geachtet werden!
  HasJob := false;
  Fin := true;
  end
  else
  Sleep(1);
  end;
end;
gut, und dann wär da noch der Timer:

Vorneweg: So wie du das implementiert hast, wird der Timer wieder auflaufen, während du noch auf's abarbeiten des letzen wartest.

Delphi-Quellcode:
procedure TForm1.Timer1Timer(Sender: TObject);
var x: Integer;
  d: Boolean;
begin
  //Den Workerthread an seine Arbeit setzen
  AWorker.SetJob(true);

  //spätestens hier erzeugst du einen deadlock, weil der Thread und du auf das selbe ASystem zugreift.
  if (OldX <> 0) and (OldY <> 0) and (MousePos_CB.Checked) then
    ASystem.AddPartikels(OldX, OldY, Emit_SB.Position)
  else
    ASystem.AddPartikels(SizeX/2, SizeY/2, Emit_SB.Position);

  Form1.Caption := 'Partiklesystem -- © by Edlmann -- ' + IntToStr(ASystem.Parts.Count) + ' Partikel';

  //An dieser Stelle wird im Spiel dann die restliche Spiellogik geupdatet

  //Auf den Thread warten
  while AWorker.GetJob do
  begin
  sleep(1);
  Application.ProcessMessages;
  end;

  //Nachdem der Thread seine Arbeit getan hat, Render die Partikel
  Render(self, d);
end;
Ich empfehle dir den Workerthread-Thread, der weiter oben schon verlinkt ist, genau zu studieren. Hier fehlt eine sinnvolle Threading struktur (wer macht was wann und warum, was muss im vordergrund passieren, was im hintergrund und vor allem: was willst du mit deinem Thread bezwecken. Denn hier bleibt nur das Programm bedienbar, wenn du denn die Synchronisation korrigierst)
Dann solltest du genau schauen, welche Speicherbereiche aus welchem Kontext geschrieben werden - Stichwort Critical section.

So kann das auf alle Fälle nicht funktionieren!

Gruß
Luggi

Edlmann 30. Okt 2011 18:04

AW: Thread will nicht synchron schwimmen?
 
@grl: Vielen Dank schonmal für dein Feedback, doch ich glaube wir reden aneinander vorbei...Das was ich da zusammengebastelt hab ist nur ein Programm, in dem mein Problem demonstriert werden soll...Das ich vergessen hab das System zu initialisieren ist ein Flüchtigkeitsfehler, der beim zusammenkopieren entstanden ist...werds mal eben überarbeiten. Hab u.a. auch eine ältere Version der Workerthreads benutzt, in der ich das True noch nicht geändert hatte...

Der Thread soll parallel zum VCL-Thread arbeiten, so dass der VCL-Thread die GameEngine übernimmt, und der Workerthread die Partikel berechnet (zumindest vorerst, vllt wenn ichs iwann mal hinbekommen hab noch mehr Threads).

Das mit dem Timer stimmt auffallend, hatte ich bisher noch nicht richtig drüber nachgedacht...was wäre denn eine bessere Möglichkeit das zu implementieren? Die Zeit, die für die Berechnungen in Anspruch genommen wird nehmen, und nur warten falls diese niedriger als 16,666ms (=> 1 Frame pro Sekunde) ist?

Lade grad eine aktualisierte Version der Demo hoch, sollten die gröbsten Dinge gefixxt sein, funktioniert aber noch immer nicht so wie ich will...Link ist der selbe wie oben.

grl 30. Okt 2011 18:51

AW: Thread will nicht synchron schwimmen?
 
Zitat:

Zitat von Edlmann (Beitrag 1133580)
@grl: Vielen Dank schonmal für dein Feedback,

Gern!

Zitat:

Zitat von Edlmann (Beitrag 1133580)
doch ich glaube wir reden aneinander vorbei...

Ich glaub nicht - denn egal was du auch erreichen willst - das was du da zusammengebastelt hast, kann nicht funktionieren.

Zitat:

Zitat von Edlmann (Beitrag 1133580)
Das was ich da zusammengebastelt hab ist nur ein Programm, in dem mein Problem demonstriert werden soll...Das ich vergessen hab das System zu initialisieren ist ein Flüchtigkeitsfehler, der beim zusammenkopieren entstanden ist...werds mal eben überarbeiten. Hab u.a. auch eine ältere Version der Workerthreads benutzt, in der ich das True noch nicht geändert hatte...

Der Thread soll parallel zum VCL-Thread arbeiten, so dass der VCL-Thread die GameEngine übernimmt, und der Workerthread die Partikel berechnet (zumindest vorerst, vllt wenn ichs iwann mal hinbekommen hab noch mehr Threads).

Dann hab ich dich schon richtig verstanden. Das machst du hier aber nicht - denn du wartest auf den Thread und damit läuft er nicht parallel.
Mal dir das mal auf - in einem Ablaufdiagramm oder so - dann siehst du das besser.


Zitat:

Zitat von Edlmann (Beitrag 1133580)
Das mit dem Timer stimmt auffallend, hatte ich bisher noch nicht richtig drüber nachgedacht...was wäre denn eine bessere Möglichkeit das zu implementieren? Die Zeit, die für die Berechnungen in Anspruch genommen wird nehmen, und nur warten falls diese niedriger als 16,666ms (=> 1 Frame pro Sekunde) ist?

Wenn du das mit Multithreading machen willst (was schon gescheit sein könnte) dann würde ich einen Thread bauen, der sich selbsttätig um die Berechnung kümmert und unabhängig ist vom Mainthread. Der Mainthread macht dann nur noch die Anzeige.

Dabei musst du aufpassen, daß du sauber getrennte Speicherbereiche verwendest oder die Zugriffe über eine CriticalSection schützt. Das ist jetzt auch das Problem warum du da nicht weiterkommst. Du schreibst einfach aus beiden Threads auf den selben speicher - das geht nicht.

Zitat:

Zitat von Edlmann (Beitrag 1133580)
Lade grad eine aktualisierte Version der Demo hoch, sollten die gröbsten Dinge gefixxt sein, funktioniert aber noch immer nicht so wie ich will...Link ist der selbe wie oben.

Nein, da ist vom wirklichen Problem nix gefixt.

Folgendes solltest du jetzt tun:
- überlege dir eine klare Trennung der Speicherbereiche. Daß du auf ASystem aus dem MainThread und aus dem WorkerThread zugreifst ohne das zu Synchronisieren (CriticalSection!) ist ein absolutes NoGo und wird nie funktionieren!!!
- Trenne klar, was welcher Thread machen muss und vermeide, daß einer auf den anderen warten muss - damit führst du das Konzept des Multithreading ad absurdum.
- Überlege dir klare Schnittstelle zwischen den beiden Teilen. z.B eine Liste der Partikel, aus der jeder Thread eines entnimmt, es bearbeitet (also berechnet/anzeigt) und es wieder zurückgibt. Das entnehmen und zurückgeben schützt zu mit einer CriticalSection. Damit ist sichergestellt, daß du nie zugleich auf eines zugreifst.

Gruß
Luggi

Edlmann 30. Okt 2011 20:01

AW: Thread will nicht synchron schwimmen?
 
Zitat:

Zitat von grl (Beitrag 1133586)
Zitat:

Zitat von Edlmann (Beitrag 1133580)
@grl: Vielen Dank schonmal für dein Feedback,

Gern!

Zitat:

Zitat von Edlmann (Beitrag 1133580)
doch ich glaube wir reden aneinander vorbei...

Ich glaub nicht - denn egal was du auch erreichen willst - das was du da zusammengebastelt hast, kann nicht funktionieren.

Zitat:

Zitat von Edlmann (Beitrag 1133580)
Das was ich da zusammengebastelt hab ist nur ein Programm, in dem mein Problem demonstriert werden soll...Das ich vergessen hab das System zu initialisieren ist ein Flüchtigkeitsfehler, der beim zusammenkopieren entstanden ist...werds mal eben überarbeiten. Hab u.a. auch eine ältere Version der Workerthreads benutzt, in der ich das True noch nicht geändert hatte...

Der Thread soll parallel zum VCL-Thread arbeiten, so dass der VCL-Thread die GameEngine übernimmt, und der Workerthread die Partikel berechnet (zumindest vorerst, vllt wenn ichs iwann mal hinbekommen hab noch mehr Threads).

Dann hab ich dich schon richtig verstanden. Das machst du hier aber nicht - denn du wartest auf den Thread und damit läuft er nicht parallel.
Mal dir das mal auf - in einem Ablaufdiagramm oder so - dann siehst du das besser.


Zitat:

Zitat von Edlmann (Beitrag 1133580)
Das mit dem Timer stimmt auffallend, hatte ich bisher noch nicht richtig drüber nachgedacht...was wäre denn eine bessere Möglichkeit das zu implementieren? Die Zeit, die für die Berechnungen in Anspruch genommen wird nehmen, und nur warten falls diese niedriger als 16,666ms (=> 1 Frame pro Sekunde) ist?

Wenn du das mit Multithreading machen willst (was schon gescheit sein könnte) dann würde ich einen Thread bauen, der sich selbsttätig um die Berechnung kümmert und unabhängig ist vom Mainthread. Der Mainthread macht dann nur noch die Anzeige.

Dabei musst du aufpassen, daß du sauber getrennte Speicherbereiche verwendest oder die Zugriffe über eine CriticalSection schützt. Das ist jetzt auch das Problem warum du da nicht weiterkommst. Du schreibst einfach aus beiden Threads auf den selben speicher - das geht nicht.

Zitat:

Zitat von Edlmann (Beitrag 1133580)
Lade grad eine aktualisierte Version der Demo hoch, sollten die gröbsten Dinge gefixxt sein, funktioniert aber noch immer nicht so wie ich will...Link ist der selbe wie oben.

Nein, da ist vom wirklichen Problem nix gefixt.

Folgendes solltest du jetzt tun:
- überlege dir eine klare Trennung der Speicherbereiche. Daß du auf ASystem aus dem MainThread und aus dem WorkerThread zugreifst ohne das zu Synchronisieren (CriticalSection!) ist ein absolutes NoGo und wird nie funktionieren!!!
- Trenne klar, was welcher Thread machen muss und vermeide, daß einer auf den anderen warten muss - damit führst du das Konzept des Multithreading ad absurdum.
- Überlege dir klare Schnittstelle zwischen den beiden Teilen. z.B eine Liste der Partikel, aus der jeder Thread eines entnimmt, es bearbeitet (also berechnet/anzeigt) und es wieder zurückgibt. Das entnehmen und zurückgeben schützt zu mit einer CriticalSection. Damit ist sichergestellt, daß du nie zugleich auf eines zugreifst.

Gruß
Luggi

Ich verstehe nicht an welcher Stelle ich aus beiden Threads etwas zugreifen soll...Der VCL-Thread berechnet die Spiellogik, der Worker die Partikel-Engine (VCL-Thread greift nicht auf die Partikel zu), nur beim Rendern liest der VCl-Thread die Werte der Partikel aus, ändert diese aber nicht. Auf den Workerthread warten hab ich eingebaut, damit der Worker auf jeden Fall beim nächsten Frame fertig mit seinem Durchlauf ist, und wieder weiter arbeiten kann. Da der Mainthread im Spiel genug zu tun hat bis dahin, sollte diese Warteschleife im Spiel nur selten aufgerufen werden, doch das schützt (zumindest in meiner eigenen kleinen Welt, in der Threads funktionieren wie ich denke :D) davor, dass Einzelschritte übersprungen werden beim Partikelsystem.

grl 30. Okt 2011 20:10

AW: Thread will nicht synchron schwimmen?
 
Zitat:

Zitat von Edlmann (Beitrag 1133603)
Ich verstehe nicht an welcher Stelle ich aus beiden Threads etwas zugreifen soll...Der VCL-Thread berechnet die Spiellogik, der Worker die Partikel-Engine (VCL-Thread greift nicht auf die Partikel zu), nur beim Rendern liest der VCl-Thread die Werte der Partikel aus, ändert diese aber nicht. Auf den Workerthread warten hab ich eingebaut, damit der Worker auf jeden Fall beim nächsten Frame fertig mit seinem Durchlauf ist, und wieder weiter arbeiten kann. Da der Mainthread im Spiel genug zu tun hat bis dahin, sollte diese Warteschleife im Spiel nur selten aufgerufen werden, doch das schützt (zumindest in meiner eigenen kleinen Welt, in der Threads funktionieren wie ich denke :D) davor, dass Einzelschritte übersprungen werden beim Partikelsystem.

Der WorkerThread macht ein
Delphi-Quellcode:
TParticleSystem(PSystem).Update;
in dem z.B. ein
Delphi-Quellcode:
Parts.Count
und ein
Delphi-Quellcode:
Parts.Items[x]
vorkommt, und der MainThread macht
Delphi-Quellcode:
ASystem.AddPartikels(OldX, OldY, Emit_SB.Position)
das dazu führt, daß die Anzahl Parts sich ändert.

Das ist nur ein beispiel - und wegen dem Timer-Problem bist du evtl. noch im MainThread im Render (das auch ein Parts.Count verwendet) während der Timer schon den nächsten Thread angeworfen hat - dann krachts gleich wieder.

Gruß
Luggi

Edlmann 30. Okt 2011 21:05

AW: Thread will nicht synchron schwimmen?
 
Zitat:

Zitat von grl (Beitrag 1133606)
Der WorkerThread macht ein
Delphi-Quellcode:
TParticleSystem(PSystem).Update;
in dem z.B. ein
Delphi-Quellcode:
Parts.Count
und ein
Delphi-Quellcode:
Parts.Items[x]
vorkommt, und der MainThread macht
Delphi-Quellcode:
ASystem.AddPartikels(OldX, OldY, Emit_SB.Position)
das dazu führt, daß die Anzahl Parts sich ändert.

Das ist nur ein beispiel - und wegen dem Timer-Problem bist du evtl. noch im MainThread im Render (das auch ein Parts.Count verwendet) während der Timer schon den nächsten Thread angeworfen hat - dann krachts gleich wieder.

Gruß
Luggi

Touche, daran habe ich gar nicht gedacht...das Partikel hinzufügen sollte also auch über den Thread laufen. Soweit so gut.
Bloß fehlt mir der Ansatz, wie ich den Timer rausnehmen kann (generell, aber noch mehr im Spiel, da es eine starke Umstrukturierung fordert). Allerdings startet der Timer ja keinen Thread, sondern sagt nur dem schon laufendem Thread "Starte den nächsten Schritt" - an der Stelle sollte es also nicht krachen.

grl 31. Okt 2011 10:42

AW: Thread will nicht synchron schwimmen?
 
Zitat:

Zitat von Edlmann (Beitrag 1133615)
...(generell, aber noch mehr im Spiel, da es eine starke Umstrukturierung fordert)

*grins* Also ich kenn kein Projekt, bei dem die Einführung von Multithreading KEINE starke Umstrukturierung erfordert.

Zitat:

Zitat von Edlmann (Beitrag 1133615)
Allerdings startet der Timer ja keinen Thread, sondern sagt nur dem schon laufendem Thread "Starte den nächsten Schritt" - an der Stelle sollte es also nicht krachen.

Naja - du stellst aber nicht sicher, daß während der Thread läuft nicht schon der nächste Timer daher kommt (was auch tatsächlich der Fall ist) - und das "kracht".

Außerdem - ich glaub da wiederhol ich mich - macht deine Implementation so generell keinen Sinn - denn ob du das in einen Thread auslagerst und dann drauf wartest oder es gleich im Hauptthread machst, macht (fast) keinen Unterschied - ganz sicher keinen in der Geschwindigkeit.

Jetzt wiederhol ich mich ganz sicher, aber:
Zitat:

Zitat von grl (Beitrag 1133586)
Folgendes solltest du jetzt tun:
- überlege dir eine klare Trennung der Speicherbereiche. Daß du auf ASystem aus dem MainThread und aus dem WorkerThread zugreifst ohne das zu Synchronisieren (CriticalSection!) ist ein absolutes NoGo und wird nie funktionieren!!!
- Trenne klar, was welcher Thread machen muss und vermeide, daß einer auf den anderen warten muss - damit führst du das Konzept des Multithreading ad absurdum.
- Überlege dir klare Schnittstelle zwischen den beiden Teilen. z.B eine Liste der Partikel, aus der jeder Thread eines entnimmt, es bearbeitet (also berechnet/anzeigt) und es wieder zurückgibt. Das entnehmen und zurückgeben schützt zu mit einer CriticalSection. Damit ist sichergestellt, daß du nie zugleich auf eines zugreifst.

Alles andere ist (zumindest meiner Erfahrung nach) Murks - und da hast du nix davon.

Gruß
Luggi

Edlmann 2. Nov 2011 16:48

AW: Thread will nicht synchron schwimmen?
 
Zitat:

Zitat von grl (Beitrag 1133666)
Zitat:

Zitat von Edlmann (Beitrag 1133615)
...(generell, aber noch mehr im Spiel, da es eine starke Umstrukturierung fordert)

*grins* Also ich kenn kein Projekt, bei dem die Einführung von Multithreading KEINE starke Umstrukturierung erfordert.

Naja - du stellst aber nicht sicher, daß während der Thread läuft nicht schon der nächste Timer daher kommt (was auch tatsächlich der Fall ist) - und das "kracht".

Außerdem - ich glaub da wiederhol ich mich - macht deine Implementation so generell keinen Sinn - denn ob du das in einen Thread auslagerst und dann drauf wartest oder es gleich im Hauptthread machst, macht (fast) keinen Unterschied - ganz sicher keinen in der Geschwindigkeit.

Jetzt wiederhol ich mich ganz sicher, aber:
Zitat:

Zitat von grl (Beitrag 1133586)
Folgendes solltest du jetzt tun:
- überlege dir eine klare Trennung der Speicherbereiche. Daß du auf ASystem aus dem MainThread und aus dem WorkerThread zugreifst ohne das zu Synchronisieren (CriticalSection!) ist ein absolutes NoGo und wird nie funktionieren!!!
- Trenne klar, was welcher Thread machen muss und vermeide, daß einer auf den anderen warten muss - damit führst du das Konzept des Multithreading ad absurdum.
- Überlege dir klare Schnittstelle zwischen den beiden Teilen. z.B eine Liste der Partikel, aus der jeder Thread eines entnimmt, es bearbeitet (also berechnet/anzeigt) und es wieder zurückgibt. Das entnehmen und zurückgeben schützt zu mit einer CriticalSection. Damit ist sichergestellt, daß du nie zugleich auf eines zugreifst.

Alles andere ist (zumindest meiner Erfahrung nach) Murks - und da hast du nix davon.

Gruß
Luggi

1.) Hab ich mir leider schon gedacht dass das nicht einfach wird... :D
2.) Nur mal so dumm gefragt, wenn ich das ProcessMessages rauslass, dürfte doch der Timer nicht ein zweites Mal aufgerufen werden befor er fertig ist, oder denk ich da gerade falsch? Denn dann wird der VCL-Thread (der ja den nächsten Timer starten müsste) ja schlafen gelegt, bis das Partikelsystem einen Schritt weiter ist (und wenn das PSystem vor der Spiellogik fertig ist wird gar nicht erst gewartet) - Das war so mein Gedankengang hinter dieser Warteschleife...
3.) Trennung der Speicherbereiche - Wenn ich alle aufrufe auf das Partikelsystem über den Worker laufen lasse, ist das ja soweit getrennt und dann sind doch Criticalsections unnötig (oder bin ich da falsch?)
- Das Warten ist ja nur als Fallback da, damit eben für den Fall dass das Partikelsystem noch nicht fertig ist eben kurz gewartet wird (es ist ja nich so dass der VCL-Thread dann nichts zu tun hätte, das ist nur im Beispielprogramm so, mit der Spiellogik dürfte er länger beschäftigt sein als der Worker mit den Partikeln)
- Trennung etc: Nicht nötig wenn ich das schon vom Code her Trenne (nur ein Thread was überhaupt an meiner Partikelliste rumbastelt) oder nicht?

Sir Rufo 2. Nov 2011 16:54

AW: Thread will nicht synchron schwimmen?
 
Zitat:

Zitat von Edlmann (Beitrag 1134083)
1.) Hab ich mir leider schon gedacht dass das nicht einfach wird... :D
2.) Nur mal so dumm gefragt, wenn ich das ProcessMessages rauslass, dürfte doch der Timer nicht ein zweites Mal aufgerufen werden befor er fertig ist, oder denk ich da gerade falsch? Denn dann wird der VCL-Thread (der ja den nächsten Timer starten müsste) ja schlafen gelegt, bis das Partikelsystem einen Schritt weiter ist (und wenn das PSystem vor der Spiellogik fertig ist wird gar nicht erst gewartet) - Das war so mein Gedankengang hinter dieser Warteschleife...
3.) Trennung der Speicherbereiche - Wenn ich alle aufrufe auf das Partikelsystem über den Worker laufen lasse, ist das ja soweit getrennt und dann sind doch Criticalsections unnötig (oder bin ich da falsch?)
- Das Warten ist ja nur als Fallback da, damit eben für den Fall dass das Partikelsystem noch nicht fertig ist eben kurz gewartet wird (es ist ja nich so dass der VCL-Thread dann nichts zu tun hätte, das ist nur im Beispielprogramm so, mit der Spiellogik dürfte er länger beschäftigt sein als der Worker mit den Partikeln)
- Trennung etc: Nicht nötig wenn ich das schon vom Code her Trenne (nur ein Thread was überhaupt an meiner Partikelliste rumbastelt) oder nicht?

Gerade das Arbeiten mit Threads nötigt einem erheblich mehr Sorgfalt ab als mit nur einem (dem MainThread).

Umsetzungen die auf der Annahme basieren "mit der Spiellogik dürfte er länger beschäftigt sein als der Worker mit den Partikeln" führen zwangsweise ins Aus (wenn nicht sofort, dann auf jeden Fall später).

Das was du machen möchtest solltest du komplett in Threads auslagern (Spiellogik, Partikel, etc.).
Der MainThread sollte nur die Anzeige regeln und die Benutzereingaben entgegennehmen.

EDIT:
BTW macht es keinen Sinn etwas in mehrere Threads auszulagern, wenn die Threads gleiche Ressourcen verwenden.

Beispiel:
Du benötigst (eine große Menge) Daten von mehreren Systemen, die über eine Leitung kommen.
Mehrere Threads werden das nicht beschleunigen können, eher wird der Thread-Overhead dieses verlangsamen

oder

Du willst umfangreiche Berechnungen durchführen. Hast du aber mehr Berechnungs-Threads als CPU-Kerne laufen, dann wird auch das langsamer anstatt schneller

aber

Du willst:
- eine Liste aller Dateien von der Festplatte sammeln (1 Thread)
- Daten von einem externen Server holen (1 Thread) - Zwischenspeicherung im RAM! sonst stören wir den Datei-Suchen-Thread ;)
- umfangreiche Berechnungen durchführen (1 Thread pro CPU)

das wird dann mit Threads schneller, als wenn du das in einem Thread (z.B. MainThread) nacheinander abarbeiten würdest.

Edlmann 2. Nov 2011 17:46

AW: Thread will nicht synchron schwimmen?
 
Zitat:

Zitat von Sir Rufo (Beitrag 1134084)
Gerade das Arbeiten mit Threads nötigt einem erheblich mehr Sorgfalt ab als mit nur einem (dem MainThread).

Umsetzungen die auf der Annahme basieren "mit der Spiellogik dürfte er länger beschäftigt sein als der Worker mit den Partikeln" führen zwangsweise ins Aus (wenn nicht sofort, dann auf jeden Fall später).

Das was du machen möchtest solltest du komplett in Threads auslagern (Spiellogik, Partikel, etc.).
Der MainThread sollte nur die Anzeige regeln und die Benutzereingaben entgegennehmen.

EDIT:
BTW macht es keinen Sinn etwas in mehrere Threads auszulagern, wenn die Threads gleiche Ressourcen verwenden.

Beispiel:
Du benötigst (eine große Menge) Daten von mehreren Systemen, die über eine Leitung kommen.
Mehrere Threads werden das nicht beschleunigen können, eher wird der Thread-Overhead dieses verlangsamen

oder

Du willst umfangreiche Berechnungen durchführen. Hast du aber mehr Berechnungs-Threads als CPU-Kerne laufen, dann wird auch das langsamer anstatt schneller

aber

Du willst:
- eine Liste aller Dateien von der Festplatte sammeln (1 Thread)
- Daten von einem externen Server holen (1 Thread) - Zwischenspeicherung im RAM! sonst stören wir den Datei-Suchen-Thread ;)
- umfangreiche Berechnungen durchführen (1 Thread pro CPU)

das wird dann mit Threads schneller, als wenn du das in einem Thread (z.B. MainThread) nacheinander abarbeiten würdest.

Soweit hab ich das Prinzip hinter den Threads schon verstanden - In der Anwendung in der ich die Threads zum laufen bekommen möchte handelt es sich wie schon gesagt um ein Spiel, was die Festplatte nur am Anfang zum Laden der Texturen beansprucht und (noch :P) auf keine Downloads aus dem Internet oder so warten muss. Dadurch ist der einzige Bottleneck die CPU, da das Spiel im Moment noch als Single-Thread-Anwendung läuft beansprucht es nur einen Kern, diesen aber zu 100% - meines Verständnis nach ein typisches Szenario in dem Multi-Threading aushelfen würde ;)

Und nur das Partikelsystem auszulagern ist auch nicht der Plan auf Dauer, das war mehr so als Versuch gedacht überhaupt mal etwas mit Threads zu machen - Mein Hauptproblem ist jedoch die vernünftige Synchronisation der Threads. Nicht die Implementierung der Threads selbst, sondern nur wie ich es hinbekomme dass mein Workerthread und der VCL-Thread (später vllt auch mehr Threads) im Gleichtakt laufen. Und da haperts halt ziemlich. Was ist dafür der Richtige Ansatz? Eigentlich doch einen Thread einmal zu erstellen und ihm dann immer neue Aufgaben zuzuteilen oder nicht? Und um diese zu synchronisieren lässt es sich doch nicht wirklich vermeiden dass der eine Thread auf den anderen wartet, sonst läuft der eine ja schneller als der andere...

grl 3. Nov 2011 12:12

AW: Thread will nicht synchron schwimmen?
 
Sorry, war grad unterwegs, daher gibts erst jetzt eine Antwort.

Erst mal: Ich denke, es gibt noch einen weiteren Grund für Threads: und zwar einfach Abläufe sauber gegeneinander abzuschotten. Das erhöht meiner Meinung nach die Stabilität und die Transparenz (wenn auch zum Preis eines höheren Implementationsaufwandes und der Gefahr, vor lauter Threads unnötig Ressourcen im Overhead zu verpulvern!)

Zitat:

Zitat von Edlmann (Beitrag 1134109)
Und nur das Partikelsystem auszulagern ist auch nicht der Plan auf Dauer, das war mehr so als Versuch gedacht überhaupt mal etwas mit Threads zu machen - Mein Hauptproblem ist jedoch die vernünftige Synchronisation der Threads. Nicht die Implementierung der Threads selbst, sondern nur wie ich es hinbekomme dass mein Workerthread und der VCL-Thread (später vllt auch mehr Threads) im Gleichtakt laufen. Und da haperts halt ziemlich. Was ist dafür der Richtige Ansatz? Eigentlich doch einen Thread einmal zu erstellen und ihm dann immer neue Aufgaben zuzuteilen oder nicht? Und um diese zu synchronisieren lässt es sich doch nicht wirklich vermeiden dass der eine Thread auf den anderen wartet, sonst läuft der eine ja schneller als der andere...

Ich denke, da liegt der Wurm in deinem Konzept: Zu wollen, daß die im Gleichtakt laufen und Multithreading ist ein Widerspruch in sich.

Ich versuchs mal an einem Projekt von mir zu erklären:

Ich hab eine Applikation, die Daten von einem Messgerät aufzeichnet, über die Aufzeichnung einige Filter drüberrechnet und das ganze dann als Kurve auf den Schirm malt.
Da hab ich mehrere Threads:
1.) die Kommunikation: Dieser Thread holt die Daten vom Messgerät, kontrolliert ob die Übertragung korrekt war, kümmert sich sonst darum daß die Wertereihe neu angefordert wird und legt dann die übertragenen Werte in einen Puffer. Außerdem hat der Thread einen Puffer, in dem an das Gerät zu übergebende Befehle drinstehen. Die werden dann vom Thread je nach freier Zeit, Zustand des Geräts und noch ein paar anderen Einflüssen versandt.

2.) Den Filter und Speicher Thread: der schaut regelmässig, ob im Puffer vom Kommunikationsthread was drin ist. Wenn ja, übernimmt er diese Wertereihe, macht die für die Filter notwendigen Berechungen und noch ein paar andere Sachen und schreibt sie in eine Datenbank.

3.) Den Anzeige-Berechnungs-Thread: Der weiß, welcher Bereich gerade auf dem Schirm angezeigt wird, holt sich die entsprechenden Basiswerte aus der Datenbank, rechnet da die für die Anzeige notwendigen Dinge und legt das Ergebnis als Werte-Paar (X und Y Wert) wieder in einen Puffer.

und 4.) der Mainthread, der die Daten aus dem Anzeige-Berechnungs-Thread-Puffer holt und anzeigt - was aber, da es fertig gerechnete X/Y-Paare sind, extrem schnell geht.

Untereinander kommunizieren diese Threads über Messages und über die Puffer. Die Puffer sind alle über Critical sections geschützt.
Dabei ist wichtig, daß die Threads nie auf die anderen Warten. Der Kommunikations-Thread muss nur seine Puffer lesen und schreiben und wenn da nix ist, was zu tun ist, dann wartet er ein bischen und versuchts wieder. Damit ist aber jederzeit sichergestellt, daß das Messgerät seine Daten loswird, wenn es welche zu senden hat.
Der Anzeige-Berechnungs-Thread z.B. bekommt vom Mainthread nur mitgeteilt, welcher Bereich gerade angezeigt werden soll und rechnet dann so lang ein Paar nach dem anderen, bis er alle Berechnet hat - unabhängig vom Mainthread und der Anzeige - außer es kommt vom Mainthread eine Änderung des Anzeigebereichs - dann fängt er von vorne an und vergisst die alte Aufgabe.
Der Mainthread wiederum schaut im Puffer des Anzeige-Berechnungs-Threads nach, ob da was ist, was er anzeigen kann und tut das dann. Dazu wird dem Mainthread vom Anzeige-Berechnungs-Thread eine Message geschickt, wenn einige Punkte gerechnet sind, damit er sie anzeigt. Wenn der Mainthread auf diese Message nicht gleich reagiert (weil er z.B. grad ein Einstellungsfenster darstellt) dann ist das für den Anzeige-Berechnungs-Thread eigentlich komplett egal - er rechnet einfach fröhlich weiter und füllt den Puffer weiter. Wenn der Mainthread dann wieder Zeit zur Anzeige hat, dann hat er eben mehrere Punkte zum anzeigen.

War das jetzt zu kompliziert? Ich hoffe nicht.

Gruß
Luggi


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