Autor Beitrag
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8548
Erhaltene Danke: 477

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: So 18.03.07 15:38 
Ich hab da mal wieder ein kleines Problem mit Threads. Folgende Situation:

Ich habe diverse Algorithmen (mehr oder weniger kompliziert) - im Laufe der Entwicklung könnten weitere dazukommen. Was die Algorithmen machen, möchte ich gerne animieren, d.h. ich habe diese Algorithmen um Animierungssequenzen erweitert. Mit Canvas.Draw und Sleep/Delay-Befehlen zur Verlangsamung. Funktioniert auch ganz gut.

Ich möchte jetzt gerne, dass mehrere Algorithmen gleichzeitig laufen und animiert werden (z.B. um zu sehen, welcher Algo schneller ist). Daher habe ich die Algorithmen-Prozeduren und Funktionen in eine Klasse TmyThread ausgelagert. Ich würde dann für jedes parallel durchzuführendes Verfahren je einen Thread erzeugen.

Die Animation läuft so ab, dass jeder Thread ein privates Bitmap besitzt, auf dem er rummalen kann. Wenn das Bild fertig zur Anzeige ist, rufe ich ein Synchronize(RefreshAnimation); auf, mit
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
type TRepaintProc = procedure(Mode: Integer; aBitmap: TBitmap; aDelay: Integer) of Object;

TmyThread = class(TThread)
private
  fRepaintProc: TRepaintProc;
//...
end;

procedure TmyThread.RefreshAnimation;
begin
  fRepaintProc(fMode, Offscreenbmp, ftmpDelaytime);
end;


Im VCL-Hauptthread sieht das ganze so aus:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
// Thread erzeugen:
// myThreads ist ein Array of TmyThread
  myThreads[0] := TmyThread.Create(True);
  myThreads[0].FreeOnTerminate := True;
  myThreads[0].RepaintProc := Form1.ReFreshPaintbox;
  myThreads[0].Resume;

procedure TForm1.ReFreshPaintbox(Mode: Integer; aBitmap: TBitmap; aDelay: Integer);
begin
  Paintbox1.Canvas.Draw(0,0,aBitmap);
end;
Ohne Animation sind die Verfahren in Nullkommanichts durch, mit Animation dauern sie einige Sekunden bis mehrere Tage. Es sollen halt Sachen wie if string1[i] = string2[j] animiert werden ;-). D.h. Synchronize wird sehr oft durchgeführt. Im Verlauf eines kompletten Durchlaufs (theoretisch) mehrere Millionen Mal, bei praktikablen kleinen Beispielen einige hundert Mal. Zwischen zwei Synchronize-Aufrufen liegen - abgesehen von der Malphase auf das Bitmap - meist nur wenige Takte.

Das läuft auch ziemlich gut durch - solange man nichts auf der Form macht. Sobald ich die Maus über der Form bewege (dann wird ja von Windows eine Message an das Fenster geschickt), dann geht die Animation kaputt, d.h. es wird mehr oder weniger sinnloses Zeug angezeigt. Irgendwie scheint die Abarbeitung der Nachrichtenqueue der Form den Thread aus dem Takt zu bringen. :nixweiss: Hat einer ne Idee, woran das liegen könnte? Es ist dabei egal, ob im Thread selbst oder in der Synchronize Methode etwas von Sleep und/oder Delay steht oder nicht.

Oder andersrum: Kann man die Threads schlafen schicken, während die Nachrichtenschleife der Form verarbeitet wird?

_________________
We are, we were and will not be.
hathor
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mo 19.03.07 12:59 
Zitat: Kann man die Threads schlafen schicken, während die Nachrichtenschleife der Form verarbeitet wird?

Im Prinzip ja - wenn man die Threads in Fibers umwandelt - habe aber damit in DELPHI noch keine Erfahrung, siehe hier:

Fibers

msdn2.microsoft.com/...ibrary/ms682661.aspx

ConvertThreadToFiber

msdn2.microsoft.com/...ibrary/ms682115.aspx

Using Fibers

msdn2.microsoft.com/...ibrary/ms686919.aspx

SwitchToFiber

msdn2.microsoft.com/...ibrary/ms686350.aspx


Zuletzt bearbeitet von hathor am Mo 19.03.07 13:17, insgesamt 1-mal bearbeitet
ZeitGeist87
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1593
Erhaltene Danke: 20

Win95-Win10
Delphi 10 Seattle, Rad Studio 2007, Delphi 7 Prof., C++, WSH, Turbo Pascal, PHP, Delphi X2
BeitragVerfasst: Mo 19.03.07 13:12 
Schlafen schicken = anhalten?

geht das nicht mit Thread.suspend:= true?

LG
Stefan

_________________
Wer Provokationen, Ironie, Sarkasmus oder Zynismus herauslesen kann soll sie ignorieren um den Inhalt meiner Beiträge ungetrübt erfassen zu können.
hathor
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Mo 19.03.07 14:05 
www.c-sharpcorner.co...Multithreading2.aspx

"You can pause/block a thread by calling Thread.Sleep or Thread.Suspend or Thread.Join. Calling the method Sleep() or Suspend() on a thread means, the thread does not get any processor time. There is a difference between these two ways of pausing a thread. Thread.Sleep causes a thread to stop immediately but the common language runtime waits until the thread has reached some safe point before calling the Suspend() method on the thread. One thread cannot call Sleep() on another thread but one thread can call Suspend() on the other thread and it causes the other thread to pause. Calling Resume() on the suspended thread breaks the thread out of the suspended state and allows it to continue execution. A single call to Resume() is sufficient to activate a thread regardless of the number of times Suspend() was called to block it. A thread that has already been terminated or has not yet started functioning cannot be suspended. Thread.Sleep(int.Infinite) causes a thread to sleep indefinitely. The thread can only wake up when it is interrupted by another thread that calls Thread.Interrupt or is aborted by Thread.Abort. You can use Thread.Interrupt to break a thread out of its blocking state but it throws a ThreadInterupptedException. You can either catch the exception, do whatever you want to do with the thread, or ignore the exception and let the run-time stop the thread. For managed wait, Thread.Interrupt and Thread.Abort both wake up the thread immediately."


Zuletzt bearbeitet von hathor am Mo 19.03.07 19:20, insgesamt 1-mal bearbeitet
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 20451
Erhaltene Danke: 2264

Win 10
C# (VS 2019)
BeitragVerfasst: Mo 19.03.07 19:13 
@hathor: Könntest Du bitte quote-Tags verwenden und vor allem eine Quelle angeben?

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8548
Erhaltene Danke: 477

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Di 20.03.07 11:32 
Das mit dem Stoppen des Threads bei der Abarbeitung der Nachrichtenschliefe war ja nur ne Idee für nen Workaround. Prinzipiell hat die Nachrichtenschleife mit dem Thread nichts zu tun. Der Thread soll zeichnen, und "ab und zu" das gezeichnete an den VCL-Thread zur Anzeige schicken - per Synchronize. Damit man auch was gucken kann, soll er dann was Pause machen. Das mache ich mit sleep(aWhile); nach dem Synchronize im Thread.

Ich hab mal das ganze Projekt in den Anhang gepackt - nicht meckern über Bedienschwierigkeiten und die Voreinstellung in dem Datei-Auswahl-Edit. Um das Problem zu sehen empfehle ich, einen Zufallstest zu starten, indem man den Start-Button per Tab auswählt und Enter drückt. Dann ne Weile laufen lassen, und schließlich mit der Maus auf der Form rumwackeln. Wer ne längere Textdatei parat hat, kann die natürlich auch nutzen.

Wär nett, wenn da mal einer mit etwas Thread-Erfahrung drübergucken könnte.

btw.: In der DP hab ich das Problem auch schon geschildert - da werde ich wohl heute Nachmittag mal pushen müssen ;-).
Einloggen, um Attachments anzusehen!
_________________
We are, we were and will not be.
ZeitGeist87
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1593
Erhaltene Danke: 20

Win95-Win10
Delphi 10 Seattle, Rad Studio 2007, Delphi 7 Prof., C++, WSH, Turbo Pascal, PHP, Delphi X2
BeitragVerfasst: Di 20.03.07 11:45 
schaut kuhl aus :)

ab und zu: wie wärs mit ner variable, die bis 1000 "zählt" und dann aktualisiert und wieder von vorn loszählt.

Aber ich schau mir jetzt mal den Quellcode an und experimentier mal *g*

LG
Stefan

_________________
Wer Provokationen, Ironie, Sarkasmus oder Zynismus herauslesen kann soll sie ignorieren um den Inhalt meiner Beiträge ungetrübt erfassen zu können.
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8548
Erhaltene Danke: 477

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Di 20.03.07 11:54 
Vielleicht ein paar Hinweise zum Code: Wichtig sind wahrscheinlich nur die Teile/Funktionen, die innerhalb der "if UseAnimation"-Blöcke stehen. Das drumherum sind die eigentlichen Algorithmen, die (hoffentlich) korrekt arbeiten. Insbesondere die PreProcess-Funktionen brauchen nicht "begutachtet" werden. Die sind etwas kniffliger und nicht auf Anhieb zu durchblicken - aber sie tun das, was sie sollen :D.

Ein Fortschritts-Indikator wird später auch noch dazu kommen, ebenso eine Geschwindigkeitssteuerung während der Thread läuft sowie "Pause" und "Stop"-Flags, damit man ggf. die Animation unterbrechen kann, um bei nem Vortrag was zu erläutern.

_________________
We are, we were and will not be.
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8548
Erhaltene Danke: 477

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Do 22.03.07 13:55 
Ich komm hier einfach nicht weiter. Diverse Experimente ändern nichts, oder erzeugen nur noch mehr Fehler. Ne ganz schlechte Idee war es, das OffScreen-Bitmap im Kontext des VCL-Hauptthreads zu erzeugen, und vom extra-Thread darauf zuzugreifen - war ja auch nicht anders zu erwarten, dass einem das um die Ohren fliegt.

Eine Verlagerung von Offscreenbmp.Create vom Create des Threads in die Execute-Routine brachte keine Änderung.

Dann hab ich mal probiert, die Paintboxen durch Images zu ersetzen, und die Repaint-Prozedur so abzuändern:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure TForm1.ReFreshPaintbox(Mode: Integer; aBitmap: TBitmap);
begin
  case Mode of
      0: Image0.Picture.Bitmap.Assign(aBitmap);
      1: Image1.Picture.Bitmap.Assign(aBitmap);
      //...
  end;
end;
Dann erhalte ich regelmäßig einen EOutOfRessources-Fehler, was laut Hilfe darauf hinweist, dass "eine Anwendung versucht, ein Fenster- oder Widget-Handle zu erzeugen, während keine Handles mehr verfügbar sind.". Über den Taskmanager lässt sich kein Ansteigen der Handles beobachten, ebenso liegen CPU- und RAM-Verbrauch in normalen Bereich und steigen nicht merkbar an.

Je länger ich mich damit beschäftige, umso weniger blick ich da mal wieder durch. :?

_________________
We are, we were and will not be.
Lossy eX
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1048
Erhaltene Danke: 4



BeitragVerfasst: Do 22.03.07 15:34 
Ich denke dein Problem liegt darin begraben, dass die GDI nicht threadsafe ist. Und ein TBitmap macht ausreichend Gebrauch von der GDI. Speziell wenn du mittels Canvasbefehlen arbeitest. Und da denke ich wird sich die GDI abschießen.

Auch wenn es jetzt so auf den ersten Blick kein Problem darstellen sollte, da ja VCL Thread und Animationsthreads jeweils ihre eigenen komplett exklusiven Bitmaps haben denke ich, dass die GDI das irgendwie Anwendungsbezogen realisiert. Ich habe keine Ahnung wie das genau intern funktioniert und wieso das so ist aber ich habe mit Bitmaps/Images in Threads immer Probleme. Irgendwann waren sie dann einfach kaputt. Ohne wirklich einen sinnvollen Grund dafür zu haben. Und deine Beschreibung spiegelt eigentlich so ziemlich genau das Gleiche wieder was ich erleben durfte.

Ich denke du wirst nicht umher kommen, dass du deine Zeichnerreien komplett im VCL Thread erledigst. Die andere Alternative wäre etwas zu implementieren was nicht die GDI benutzt. Die Graphics32 machen ja doch meines Wissens nach so ziemlich alles per Hand. Wobei du da mit deinen Buchstaben aufpassen müsstest. Wenn die das können werden die dazu wohl auch wieder GDI bemühen. Evtl ist die GDI+ ja Threadsafe? Aber damit kenne ich mich nicht aus.

PS: Wenn du 3 Threads hast die unterschiedliche Sachen berechnen. Laufen die nicht irgendwann trotzdem ein bisschen auseinander? Selbst wenn du sie ein bisschen schlafen legst würden die einzelnen Berechnungsschritte ja unterschiedliche zeit benötigen bzw könnte es auch durch das Synchronize passieren, dass sie unterschiedlich lange warten müssen und so wiederrum ein anderer bevorteilt werden würde. Nur so ein Gedanke. Ich kenne die Algorithmen nicht und auch nicht wie das intern alles arbeitet.

_________________
Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8548
Erhaltene Danke: 477

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Do 22.03.07 15:53 
Na, das ist ja mal ein Ansatz. Das bedeutet also: nicht nur "VCL + Thread = Böse", sondern auch "Bitmap + Thread = Böse"? Werde dann mal versuchen, das Zeichnen komplett in den VCL-Thread zu verlagern. Aber erstmal mach ich n bissel Theorie weiter.

Zum "Auseinandergehen". Die Laufzeiten, die durch die Animation gewissermaßen visuell ermittelt weden können, lassen natürlich nur sehr begrenzt Rückschlüsse über die tatsächliche Laufzeit zu. Z.B. dauert im untersten Algorithmus der Shiftvorgang relativ lange (damit mans halt sieht, was da passiert), aber im Code ist das nur eine Zuweisung wie  k := k + shift;. Unterschiedliche Wartezeiten zum Synchronisieren sind da das kleinste Problem. Die Algorithmen an sich laufen auch auf ner 4MB-Textdatei (jup, die Bibel besteht aus gut 4 Millionen Buchstaben) ohne sichtbare Laufzeit (d.h. man klickt auf suchen, und das Ergebnis ist "sofort" da). Ein kompletter Durchgang mit Animation würde da wahrscheinlich mehrere Tage dauern. Aber darum gehts auch nicht ;-)

_________________
We are, we were and will not be.
hathor
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Do 22.03.07 16:00 
Source lässt sich compilieren - trotz Synchronize-Error.
Aber sonst macht es im Textfile-Modus nichts "sinnvolles".
Ich habe BDS2006.
Woher kommt der Synchronize-Error? = Befehl ist undefined.
Einloggen, um Attachments anzusehen!
Lossy eX
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 1048
Erhaltene Danke: 4



BeitragVerfasst: Do 22.03.07 16:22 
Das ist schon ein bisschen her aber ich meine es noch so im Kopf zu haben. Die GDI ist nur bedingt Threadsafe. Eigentlich ja aber nur wenn die Objekte ihren Thread nicht verlassen. Sobald sie das tun kann man für nichts mehr garantieren. Aber beim Darstellen in einem anderen Fenster müssen sie das aber. Aber ob ich mich da so darauf verlassen würde. Ich weiß nicht. Besser nicht. Das ist immer noch am Sichersten.

_________________
Nur die Menschheit ist arrogant genug, um zu glauben sie sei die einzige intelligente Lebensform im All. Wo nicht mal das nachhaltig bewiesen wurde.
turboPASCAL
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 193
Erhaltene Danke: 1

Win XP / Vischda
D6 PE / D2005 PE
BeitragVerfasst: Do 22.03.07 18:07 
Aha, soso...

Ich wollt dir noch sagen das du dir noch eine Variable in deine Threads einbauen solltest
die zB nach dem Beenden jener auf True gesetzt werden. Sozusagen als Fertischmeldung.

Mann kann ja im Moment die Threads starten so oft man will. auch 10 mal das heist in der
App laufen dann 3 * 10 Threads :) :gruebel:
Gausi Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8548
Erhaltene Danke: 477

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Do 22.03.07 18:43 
Dass die GUI nicht idiotensicher ist, hatte ich ja schon gesagt. Natürlich kann man zur Zeit noch wild auf "Suche starten" klicken, und es werden dutzende Threads gestartet, die sich gegenseitig übermalen. Das wird natürlich noch geändert, indem vorher alte Threads terminiert werden. Ebenso kommen evtl. Interaktionen in den Ablauf des Algorithmus hinzu. Aber das war ja nicht das Problem dieses Topics.

Das mit dem Zeichnen im VCL-Thread statt im Thread selber scheint zu funktionieren. Zumindest der naive Algorithmus läuft jetzt schön in vierfacher Ausfertigung auf der Form durch. An die anderen Algos mach ich mich dann jetzt mal ran.

Danke - Problem erstmal gelöst :D.

_________________
We are, we were and will not be.