Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Thread Synchronize Fragen (https://www.delphipraxis.net/193263-thread-synchronize-fragen.html)

Glados 10. Jul 2017 09:10

Thread Synchronize Fragen
 
Ich versuche seit ein paar Tagen meinen Thread zu optimieren, sodass ich ohne Synchronize arbeiten kann. Hierzu ein paar Fragen:

- muss ich das Setzen eines Tray-Icon-Hints synchronisieren?

- wie kann ich Position und Hint einer ProgressBar ohne Synchronize setzen?
Ich habe meinem Thread das Handle meiner ProgressBar übergeben und dann folgendes versucht
Delphi-Quellcode:
ProgressBarHandle:; THandle;
SendMessage(ProgressBarHandle, PBM_SETPOS, 55, 0);
Leider tut sich dabei exakt nichts! PostMessage funktioniert auch nicht.
Aktuell verwende ich
Delphi-Quellcode:
TThread.ForceQueue()
statt Synchronize.

Uwe Raabe 10. Jul 2017 09:39

AW: Thread Synchronize Fragen
 
Synchronize blockiert deinen Thread bis der Befehl abgearbeitet wurde. Für das Setzen eines Hints oder eine Fortschrittsanzeige ist diese Synchronizität aber gar nicht notwendig. Hier kannst du auch einfach
Delphi-Quellcode:
Queue
einsetzen, was deinen Thread eben nicht blockiert, sonst aber dasselbe macht.
Delphi-Quellcode:
ForceQueue
ist nur dann sinnvoll, wenn der Aufruf sowohl aus einem Neben- als auch aus dem Hauptthread erfolgen kann.

Glados 10. Jul 2017 09:39

AW: Thread Synchronize Fragen
 
Kann man also festhalten Queue ist besser als Synchronize?
Zitat:

Für das Setzen eines Hints oder eine Fortschrittsanzeige ist diese Synchronizität aber gar nicht notwendig
Heißt das, ich kann FormX.ProgressBarY.Position := in einem Thread ohne Synchronize aufrufen und komplett auf Queue ausweichen?

Verstehe ich das richtig?
Synchronize synchronisiert sofort und erst wenn das fertig ist geht es nach meinem Befehl, der in Synchronize() steht weiter
und Queue packt den Aufruf in eine ART Stack, es geht aber sofort weiter?

Nur kann Queue dann nicht Probleme machen wenn man folgendes Szenario hat?

Delphi-Quellcode:
var i: Integer;

i := 2;

TThread.Queue(nil, procedure begin i := 5; end);

if i = 5 then
 begin
 end;
Könnte es denn jetzt passieren, dass
Delphi-Quellcode:
i := 5
ins Queue gesetzt wird aber erst so spät ausgeführt wird, dass
Delphi-Quellcode:
if i := 5
nicht zutrifft, da i ja noch 2 ist?
Oder denke ich hier falsch?

Der schöne Günther 10. Jul 2017 09:48

AW: Thread Synchronize Fragen
 
Richtig,
Delphi-Quellcode:
Queue()
ist asynchron,
Delphi-Quellcode:
Synchronize()
ist synchron (höhö)

PS: Doch, genau in dem Fall willst du ganz sicher NICHT einen asynchronen Aufruf verwenden. Denn das Setzen auf fünf passiert praktisch garantiert NACH deiner Abfrage.

Glados 10. Jul 2017 09:49

AW: Thread Synchronize Fragen
 
Könnte mein oben genanntes Problem denn eintreten?
i := 5 ist nur ein Beispiel.

Der schöne Günther 10. Jul 2017 10:19

AW: Thread Synchronize Fragen
 
Du hast zwei Möglichkeiten: Synchron (
Delphi-Quellcode:
TThread.Synchronize(..)
) und asynchron (
Delphi-Quellcode:
TThread.Queue(..)
)

Wenn du die Auswirkungen/Resultate/Rückgaben des Codes den du im Kontext eines anderen Threads ausführen möchtest direkt brauchst, dann brauchst du
Delphi-Quellcode:
TThread.Synchronize(..)

Das "Problem" wird garantiert auftreten, hier noch einmal das gleiche Beispiel, nur etwas ausführlicher:
Delphi-Quellcode:
procedure TForm17.Button1Click(Sender: TObject);
begin
   TThread.CreateAnonymousThread(
      procedure()
      var
         capturedVariable: Integer;
      begin
         capturedVariable := 100;
         TThread.Queue(
            nil,
            procedure()
            begin
               capturedVariable := 42;
            end
         );

         case capturedVariable of
            100:
               Beep();
            42:
               begin
                  Beep();
                  Beep();
               end;
         else
            Beep(); Beep(); Beep();
         end;
      end
   ).Start();
end;

himitsu 10. Jul 2017 10:22

AW: Thread Synchronize Fragen
 
SendMessage und PostMessage synchronisieren sich selbstständig,
die Messages werden im Erstellungsthread der Komonenten verarbeitet, also innerhalb der MessageLoop. (bei der VCL im Hauptthread).

PostMessage ist asynchron (ist wie Queue)
SendMessage ist synchron (wie Synchronize)

Achtung: TThread.Queue im Haupthtread aufgerufen ist synchron, also wie Synchronize (k.A. wer da auf diese schwachsinnige Idee gekommen ist)
[edit] Seit wann gibt es ForceQueue?

Der schöne Günther 10. Jul 2017 10:28

AW: Thread Synchronize Fragen
 
Seit 10.1 Berlin 8-)

Glados 10. Jul 2017 10:30

AW: Thread Synchronize Fragen
 
Zitat:

Das "Problem" wird garantiert auftreten, hier noch einmal das gleiche Beispiel, nur etwas ausführlicher:
Danke für das Beispiel. Dann werde ich Queue nur da anwenden, wo ich die Daten nicht sofort brauche (das heißt GUI-Änderungen und keine Variablen die ich unmittelbar nach dem Queue brauche).

Zitat:

SendMessage und PostMessage synchronisieren sich selbstständig,
Woran kann das den liegen, dass sich hier rein gar nichts tut? Laut diesem Beitrag soll es ja angeblich funktionieren https://www.entwickler-ecke.de/topic..._106970,0.html

himitsu 10. Jul 2017 10:35

AW: Thread Synchronize Fragen
 
CriticalSection
MultiReaderSingleWriter
Interlocked


Interlocked: Setzen von Integer, Booleans (LongBool) usw., also allem mit "4 Byte", bzw. in Registergröße.
Delphi-Referenz durchsuchenInterlockedIncrement/MSDN-Library durchsuchenInterlockedIncrement, InterlockedDecrement, InterlockedExchange, ..., bis hin zu Assembler (LOCK)

Glados 10. Jul 2017 10:37

AW: Thread Synchronize Fragen
 
Zitat:

CriticalSection
MultiReaderSingleWriter
Interlocked
Ich verstehe nicht, was das jetzt mit SendMessage und der ProgressBar zu tun hat.

Der schöne Günther 10. Jul 2017 10:39

AW: Thread Synchronize Fragen
 
Zitat:

Zitat von Glados (Beitrag 1376351)
Woran kann das den liegen, dass sich hier rein gar nichts tut? Laut diesem Beitrag soll es ja angeblich funktionieren https://www.entwickler-ecke.de/topic..._106970,0.html

Ich habe das jetzt nicht alles gelesen, aber das hier scheint für mich bestens zu funktionieren:
Delphi-Quellcode:
unit Unit17;

interface uses
   System.SysUtils, System.Classes,
   Winapi.Windows,
   Vcl.Controls, Vcl.Forms, Vcl.StdCtrls, Vcl.ComCtrls;

type
   TMyThread = class(TThread)
      protected var
         progressbarHandle: THandle;
      protected
         procedure Execute(); override;
      public
         constructor Create(const progressbarHandle: THandle);
    end;

   TForm1 = class(TForm)
      ProgressBar1: TProgressBar;
      Button1: TButton;
      procedure Button1Click(Sender: TObject);
   end;

var
   Form1: TForm1;

implementation uses Winapi.CommCtrl;

{$R *.dfm}

{ TMyThread }

constructor TMyThread.Create(const progressbarHandle: THandle);
begin
   inherited Create(True);
   self.progressbarHandle := progressbarHandle;
end;

procedure TMyThread.Execute();
var
   percentDone: Integer;
begin
   percentDone := 0;

   while (not Terminated) do
      begin
         Inc(percentDone, 1);

         PostMessage(progressbarHandle, PBM_SETPOS, percentDone, 0);
         if (percentDone < 100) then
            TThread.Sleep(100)
         else
            Terminate();
      end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
   myThread: TMyThread;
begin
   myThread := TMyThread.Create(ProgressBar1.Handle);
   myThread.FreeOnTerminate := True;
   myThread.Start();
end;

end.

Bonuspuntke für mehrmaliges Drücken des Buttons :wink:

himitsu 10. Jul 2017 10:45

AW: Thread Synchronize Fragen
 
Zitat:

Zitat von Glados (Beitrag 1376355)
Ich verstehe nicht, was das jetzt mit SendMessage und der ProgressBar zu tun hat.

Hier gab es auch genügend andere Beispiele mit Variablen, du hattest einmal von "Variablen" gesprochen und auch im Titel geht es allgemain um Synchronisierung.

Glados 10. Jul 2017 10:46

AW: Thread Synchronize Fragen
 
Funktioniert bei mir leider trotzdem nicht.

Der schöne Günther 10. Jul 2017 10:47

AW: Thread Synchronize Fragen
 
(Hier könnte ihre Werbung stehen)

p80286 10. Jul 2017 10:52

AW: Thread Synchronize Fragen
 
Zitat:

Zitat von Glados (Beitrag 1376355)
Ich verstehe nicht, was das jetzt mit SendMessage und der ProgressBar zu tun hat.

Du hast im Hauptformular ein Progressbar (MainThread), und einen Thread der "Datenschaufelt". Dieser sendet per SendMessage/PostMessage an das Hauptformular immer wieder eine Statusmeldung mit dem aktuellen Zählerstand. Wenn das Hauptformular Zeit hat wird es dann die Progressbar aktualisieren.

So ist die grobe Richtung.

Gruß
K-H

Glados 10. Jul 2017 10:53

AW: Thread Synchronize Fragen
 
Zitat:

Wenn das Hauptformular Zeit hat wird es dann die Progressbar aktualisieren.
Liegt das vielleicht an meinem Code?
Pseudo!

Delphi-Quellcode:
- MainForm > Button1Click
-- UnitX.Prozedur1 wird aufgerufen*
-- while-Schleife**
-- ein paar Sachen werden noch in UnitX.Prozedur1 erledigt
- wenn fertig, gehts zurück zu Button1Click (Aufrufer), ein paar Sachen werden noch erledigt und der Code ist beendet.
* diese Arbeit kann etwas dauern. In dieser Prozedur1 wird auch mein Thread erst erzeugt.
**Nachdem der Thread erzeugt wurde warte ich mit
Delphi-Quellcode:
while ... solange der thread läuft ... do begin Application.ProcessMessages; Sleep(1000); end;

TiGü 10. Jul 2017 11:43

AW: Thread Synchronize Fragen
 
Wozu dann der Thread, wenn du eh Sleep im Mainthread aufrufst? Dann kannste auch alles gleich darin (im Button1Click) ausführen.

p80286 10. Jul 2017 11:45

AW: Thread Synchronize Fragen
 
Zitat:

Zitat von TiGü (Beitrag 1376368)
Wozu dann der Thread, wenn du eh Sleep im Mainthread aufrufst? Dann kannste auch alles gleich darin (im Button1Click) ausführen.

Vermutung, weil er etwas in einen Thread ausgelagert hat, und sonst nicht weiß wie er "zurück" kommen soll?

Gruß
K-H

Glados 10. Jul 2017 11:45

AW: Thread Synchronize Fragen
 
Das ist sehr alter Code.

Ohne die Threads (es gibt mehrere Worker-Threads) würde das gar nicht so funktionieren wie ich es möchte und wie es aktuell auch ist.

TiGü 10. Jul 2017 11:48

AW: Thread Synchronize Fragen
 
Zitat:

Zitat von Glados (Beitrag 1376370)
Ohne die Threads (es gibt mehrere Worker-Threads) würde das gar nicht so funktionieren wie ich es möchte und wie es aktuell auch ist.

Hast du mal Günthers Beispiel aus Beitrag #12 ausprobiert?

Glados 10. Jul 2017 11:49

AW: Thread Synchronize Fragen
 
Ja. Wenn ich das so alles in meine MainForm packe funktioniert es. Deswegen ja meine Vermutung mit der While-Schleife.

p80286 10. Jul 2017 11:49

AW: Thread Synchronize Fragen
 
Zitat:

Zitat von Glados (Beitrag 1376370)
Das ist sehr alter Code.

Ohne die Threads (es gibt mehrere Worker-Threads) würde das gar nicht so funktionieren wie ich es möchte und wie es aktuell auch ist.

Naja so ganz unproblematisch ist die Sache ja wohl nicht, aber im Augenblick ist das eher ein herumstochern im Nebel als Lösung eines konkreten Problems.

Gruß
K-H

Glados 10. Jul 2017 11:53

AW: Thread Synchronize Fragen
 
Ich habe gerade etwas getestet und etwas herausgefunden.
Jeglicher Code der nach der While-Schleife (die ja nach der Erzeugung der Threads kommt) kommt, wurde jetzt weggelassen.
Button1Click wird ebenfalls per Exit nach Aufruf von UnitX.Prozedur1 sofort verlassen. Selbst dann funktioniert PostMessage nicht.

Dahingegen funktioniert im Thread aber sogar ein unsynchronisierted/un-queu'tes
Delphi-Quellcode:
FrmMain.Caption := IntToStr(i);


Könnte es daran liegen, dass ich ein TApplicationEvents auf dem Form habe und ApplicationEvents1Message verwende?

p80286 10. Jul 2017 12:05

AW: Thread Synchronize Fragen
 
Zitat:

Zitat von Glados (Beitrag 1376376)
Ich habe gerade etwas getestet und etwas herausgefunden.
Jeglicher Code der nach der While-Schleife (die ja nach der Erzeugung der Threads kommt) kommt, wurde jetzt weggelassen.
Button1Click wird ebenfalls per Exit nach Aufruf von UnitX.Prozedur1 sofort verlassen. Selbst dann funktioniert PostMessage nicht.

Woher weiß Du das?
Das Postmessage braucht eine Gegenseite, die die Message annimmt, und dann wird immer noch eine Anzeige benötigt.

Zitat:

Zitat von Glados (Beitrag 1376376)
Dahingegen funktioniert im Thread aber sogar ein unsynchronisierted/un-queu'tes
Delphi-Quellcode:
FrmMain.Caption := IntToStr(i);

Zufall, geht 10.000 mal gut und dann macht es *Bumm*

Gruß
K-H

Glados 10. Jul 2017 12:07

AW: Thread Synchronize Fragen
 
Zitat:

Das Postmessage braucht eine Gegenseite, die die Message annimmt,
Das lese ich zum ersten mal.

Ich verwende eh schon TApplicationMessage. Kann ich da die PBM_SETPOS-Message abfangen?

jaenicke 10. Jul 2017 12:19

AW: Thread Synchronize Fragen
 
Zitat:

Zitat von Glados (Beitrag 1376365)
Liegt das vielleicht an meinem Code?
Pseudo!

Wie wäre es, wenn du dem Thread eine anonyme Methode übergibst und darin den Code ausführst, der nach der Abarbeitung der Threadaktionen laufen soll. Dann sparst du dir das ganze Warten usw.

p80286 10. Jul 2017 12:20

AW: Thread Synchronize Fragen
 
Zitat:

Zitat von Glados (Beitrag 1376381)
Zitat:

Das Postmessage braucht eine Gegenseite, die die Message annimmt,
Das lese ich zum ersten mal.

"BOOL PostMessage(

HWND hWnd, // handle of destination window
UINT Msg, // message to post
WPARAM wParam, // first message parameter
LPARAM lParam // second message parameter
);"

Was glaubst Du verbirgt sich hinter handle of destination window?

und dort wird die Message verarbeitet, und sei es durch ignorieren....

Gruß
K-H

Glados 10. Jul 2017 12:21

AW: Thread Synchronize Fragen
 
Ich weiß jetzt nicht genau wie das funktionieren soll, da ich einerseits in Button1Click am Ende etwas ausführe aber auch schon vorher nach der While-Schleife in UnitX.Prozedur1.
Einfach 2x das was du sagtest?

Zitat:

Was glaubst Du verbirgt sich hinter handle of destination window?
Ich betone es noch einmal. Ich habe nur diesen Code hier abgeguckt mehr nicht https://www.entwickler-ecke.de/topic..._106970,0.html
Von mehr habe ich auch keine Ahnung.

Zitat:

und dort wird die Message verarbeitet, und sei es durch ignorieren....
ich habe keine Ahnung wo dort sein soll.


Es funktioniert jetzt. Ich habe vorher laut Entwickler-Ecke das Handle der Progressbar übergeben.

Glados 12. Jul 2017 13:46

AW: Thread Synchronize Fragen
 
Ich habe jetzt soweit alle Synchronize und TTHread.Queue entfernt und durch PostMessage ersetzt.
Um das alles ordentlich handeln zu können habe ich ein paar const angelegt, die ich den PostMessage übergebe und dann in WndProc mit einem Case auswerte.
Das Case hat 7 Blöcke.

Ist das schon besser als für alles ein Synchronize zu verwenden? Die Bedienung der GUI fühlt sich, wenn Threads laufen und PostMessage's abschicken statt Synchronize ausführen, jetzt in jedem Fall wesentlich "flüssiger" an. Man merkt gar nicht mehr, dass im Hintergrund etwas arbeitet.

Meine Konstanten sind aber noch fragwürdig.
Delphi-Quellcode:
... = WM_USER + 1;
... = WM_USER + 2;
... = WM_USER + 3;
Geht das nicht besser als mit
Delphi-Quellcode:
WM_USER + N;
?

Glados 12. Jul 2017 21:42

AW: Thread Synchronize Fragen
 
Blub :lol: (siehe Seite 3)

Uwe Raabe 12. Jul 2017 22:40

AW: Thread Synchronize Fragen
 
Zitat:

Zitat von Glados (Beitrag 1376542)
Geht das nicht besser als mit
Delphi-Quellcode:
WM_USER + N;
?

Wenn du bei
Delphi-Quellcode:
PostMessage
bleiben willst, nein. Ich würde
Delphi-Quellcode:
TThread.Queue
verwenden. Das ist wesentlich besser lesbar und deutlich flexibler.

Glados 13. Jul 2017 11:00

AW: Thread Synchronize Fragen
 
Zitat:

Wenn du bei PostMessage bleiben willst, nein. Ich würde TThread.Queue verwenden. Das ist wesentlich besser lesbar und deutlich flexibler.
Aber synchronisiert Queue nicht auch irgendwo wieder das Hauptformular komplett?

Es ist besser lesbar und dafür gibt es 100 Punkte mehr als für PostMessage. Aber PostMessage, muss man leider sagen, ist wesentlich performanter wenn man das mehrere Tausend mal in 20 Sekunden ausführt.

Uwe Raabe 13. Jul 2017 12:45

AW: Thread Synchronize Fragen
 
Zitat:

Zitat von Glados (Beitrag 1376632)
Aber synchronisiert Queue nicht auch irgendwo wieder das Hauptformular komplett?

Was meinst du damit? Natürlich wird die Queue-Proc im Hauptthread aufgerufen, genau wie auch bei Synchronize. Der Unterschied ist, daß Synchronize wartet bis dies geschehen ist und Queue eben nicht.

Zitat:

Zitat von Glados (Beitrag 1376632)
Es ist besser lesbar und dafür gibt es 100 Punkte mehr als für PostMessage. Aber PostMessage, muss man leider sagen, ist wesentlich performanter wenn man das mehrere Tausend mal in 20 Sekunden ausführt.

Das mit der Performance kann ich ohne konkreten Anwendungsfall nicht beurteilen. Der Ablauf mit Queue ist aber im Prinzip folgender:

Die auszuführende Prozedur wird per Queue in eine Liste geschrieben. Dann wird der Hauptthread aufgeweckt indem ihm per PostMessage (sieh mal an!) ein WM_NULL geschickt wird. In der Application.WndProc wird diese Message dann mit einem CheckSynchronize verarbeitet. Das CheckSynchronize arbeitet dann alle aktuell in der Liste stehenden Queue-Procs ab. Gegenüber einer reinen PostMessage-Lösung könnte sich hier eigentlich nur die Verwaltung der Liste auf die Performance auswirken.

jaenicke 13. Jul 2017 18:10

AW: Thread Synchronize Fragen
 
Zitat:

Zitat von Glados (Beitrag 1376632)
Es ist besser lesbar und dafür gibt es 100 Punkte mehr als für PostMessage. Aber PostMessage, muss man leider sagen, ist wesentlich performanter wenn man das mehrere Tausend mal in 20 Sekunden ausführt.

Wir haben zufällig vor einiger Zeit eine alte Message-basierte Lösung auf TThread.Queue umgestellt.
Der Quelltext ist dadurch sehr viel kürzer und übersichtlicher geworden und wurde von der Performance her tendentiell eher etwas schneller. Einen großen eindeutig messbaren Unterschied gab es jedenfalls nicht und damit gewinnt der bessere Code.

Glados 13. Jul 2017 20:14

AW: Thread Synchronize Fragen
 
Zitat:

Das mit der Performance kann ich ohne konkreten Anwendungsfall nicht beurteilen.
Beispiel 1: gegeben ist ein Thread welcher sich alle 25ms die Position einer ProgressBar holt.
Beispiel 2: gegeben ist ein Thread der alle 25ms die Position einer ProgressBar setzt.

Gerade bei Beispiel 2 konnte ich große Unterschiede bzgl eines ruckelfreien Hauptformulars feststellen.

himitsu 13. Jul 2017 20:18

AW: Thread Synchronize Fragen
 
Beispiel 3: der Thread ist schlau genug und muß das nicht alle 25ms tun

Glados 13. Jul 2017 20:19

AW: Thread Synchronize Fragen
 
Mein Thread ist schlau genug :P er prüft vorher, ob sich die ProgressBar überhaupt bewegt hat :P ob es überhaupt etwas zu ändern gibt, indem ein paar Daten abgeglichen werden.

Zusätzlich habe noch so etwas drin
Delphi-Quellcode:
if (System.DateUtils.MilliSecondsBetween(Now, iCurrentTime) >= 500) then
 begin
  // hier jetzt prüfen, ob ProgressBar überhaupt gesetzt werden muss
 
  iCurrentTime := Now;
 end;
Ich komme jedenfalls gut klar mit den PostMessages. Ich finde es nun sauberer und perfomanter.


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