AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Verständnisfrage zur Thread-Synchronisation

Ein Thema von EdAdvokat · begonnen am 10. Apr 2022 · letzter Beitrag vom 22. Apr 2022
Antwort Antwort
Seite 1 von 5  1 23     Letzte »    
EdAdvokat

Registriert seit: 1. Mai 2016
Ort: Berlin
419 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#1

Verständnisfrage zur Thread-Synchronisation

  Alt 10. Apr 2022, 17:29
Delphi-Version: 10.4 Sydney
Hallo zusammen,
ich beschäftige mich vermutlich mit mäßigem Erfolg mit Threads und habe dazu ein Beispiel hier aus dem Forum ausgewählt, dass unlängst hier diskutiert wurde.
https://www.delphipraxis.net/210313-...d-starten.html
Ich habe also das von #Sinspin gepostete Beispiel für mich zum Verständnis ausgewählt.
Da wird also ein Haupt-Thread erzeugt, in dem eine Schleife bis 100 zählt und diese dann an ein Counter.Label sendet.
Als weiterer Thread wird ein Activityindicator erzeugt. Beide Threads laufen parallel.
Nun wollte ich das Beispiel erweitern und einen weiteren Hintergrund-Thread hinzufügen und erhalte folgenden Effekt:
Der 2. Thread mit einem weiteren Activityindicator und einer Schleife, die bis 70 zählt wird sofort ausgeführt (zählt bis 70 und 2. Activityindicator wird angezeigt).
Ebenfalls parallel wird der 1. Activityindicator ausgeführt.
Erst wenn die 70-ger-Schleife (2.Thread) abgearbeitet wurde startet die Schleife bis 100 (Haupt-Thread) und beide Activityindicatoren werden parallel angezeigt.
Ist das so richtig?
Wie muß man ein Konstrukt mit einem Haupt-Thread und zwei weiteren Threads bauen? Ich bin da so völlig unbedarft und taste mich an die ominösen Threads erst heran.
Die hier oft diskutierten Beispiele sind mir oft zu kompliziert um nur das Wesen der Threads zu begreifen und natürlich auch deren Synchronisation.
Nun noch mein Erweiterungs-Konstruckt auf 2 Threads neben dem Haupt-Thread:

Delphi-Quellcode:
unit Unit1;

interface
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Threading,
  System.Classes, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
  Vcl.WinXCtrls, Vcl.ExtCtrls;

type
  TheThread = class(TThread)
  private
    procedure DoIt;
  public
    procedure Execute; override;
  end;

type
  TheThread2 = class(TThread)
  private
    procedure DoIt2;
  public
    procedure Execute; override;
  end;

type
  TForm1 = class(TForm)
  actvtyndctr1: TActivityIndicator;
  CounterLabel: TLabel;
  StartThreadBtn: TButton;
  btnclose: TButton;
    lblDoit2: TLabel;
    actvtyndctr2: TActivityIndicator;
    lblThread2: TLabel;
    lblThread1: TLabel;
    bvl1: TBevel;
    lblHauptThread: TLabel;
  procedure StartThreadBtnClick(Sender: TObject);
  procedure btncloseClick(Sender: TObject);
  private
    { Private-Deklarationen }

  public
    { Public-Deklarationen }

  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TheTread1 }

procedure TheThread.DoIt;
begin
  Form1.actvtyndctr1.Animate := true;
end;

procedure TheThread.Execute;
begin
  Synchronize(DoIt);
end;

{ TheTread2 }

procedure TheThread2.DoIt2;
var i: int32;
begin
  Form1.actvtyndctr2.Animate:= True;

  for i := 0 to 70 do
  begin
      Form1.lblDoIt2.Caption := I.tostring;
      Application.ProcessMessages;
      Sleep(30);
    end;

end;

procedure TheThread2.execute;
begin
  inherited;
  Synchronize(DoIt2);
end;

procedure TForm1.btncloseClick(Sender: TObject);
begin
  Close;
end;

procedure TForm1.StartThreadBtnClick(Sender: TObject);
var
  Thread: TheThread;
  Thread2: TheThread2;
  I: integer;
begin
  try
    // Thread starten
    Thread := TheThread.Create(True);
    Thread.FreeOnTerminate := true;
    Thread.Start;

    Thread2:=TheThread2.Create(True);
    thread2.FreeOnTerminate:=True;
    thread2.Start;

    // Hauptthread Zählt bis 100 u. gibt Ergebnis aus
    for I := 0 to 100 do
    begin
      Form1.CounterLabel.Caption := I.tostring;
      Application.ProcessMessages;
      Sleep(50);
    end;
    Form1.actvtyndctr1.Animate := false;
    Form1.actvtyndctr2.Animate := false;
  except
    on E:Exception do
    begin
      Form1.actvtyndctr1.Animate := false;
      Form1.actvtyndctr2.Animate := false;
    end;
  end;
end;
end.
Anbei das Beispielprogramm
Angehängte Dateien
Dateityp: zip a_Thread3.zip (25,1 KB, 1x aufgerufen)
Norbert
  Mit Zitat antworten Zitat
DieDolly

Registriert seit: 22. Jun 2018
2.175 Beiträge
 
#2

AW: Verständnisfrage zur Thread-Synchronisation

  Alt 10. Apr 2022, 17:38
Zitat:
Erst wenn die 70-ger-Schleife (2.Thread) abgearbeitet wurde startet die Schleife bis 100 (Haupt-Thread) und beide Activityindicatoren werden parallel angezeigt.
Ist das so richtig?
Eigentlich sollte die Schleife bis 100 sofort loslegen wenn der 2. Thread gestartet wurde.
Wenn das so funktionieren soll, wie du oben beschreibst, dann braucht man ja keinen Thread und kann alles nacheinander abarbeiten.

Außerdem würde ich Form1.actvtyndctr2.Animate := false; nicht da stehen lassen wo es steht.
Das soll da aufgerufen werden, wo auch der Thread seine Arbeit erledigt hat. Also im Thread selber und nicht außerhalb.

Dein Beispiel ist nicht gut, ich würde das wegschmeißen und was Vernünftiges suchen.

Geändert von DieDolly (10. Apr 2022 um 17:53 Uhr)
  Mit Zitat antworten Zitat
BerndS

Registriert seit: 8. Mär 2006
Ort: Jüterbog
491 Beiträge
 
Delphi 12 Athens
 
#3

AW: Verständnisfrage zur Thread-Synchronisation

  Alt 10. Apr 2022, 19:23
Was dir anschließend noch nicht klar ist:
1. Im Execute macht man das,was zu tun ist.
Also for... Slepp usw.
2. Per synchronise erfolt der Zugriff auf die Vcl und hier kein Sleep!
3. Application.ProcessMessages sollte bzw. darf weder im Excecute noch in den per
Synchronise abgerufenen Methoden verwendet werden.
4. Eine Exception Behandlung muss im Execute erfolgen.

Ich bin grade an Tablett und kann daher kein Beispiel machen, wie es sinnvoll wäre.
  Mit Zitat antworten Zitat
Benutzerbild von Phoenix
Phoenix
(Moderator)

Registriert seit: 25. Jun 2002
Ort: Hausach
7.641 Beiträge
 
#4

AW: Verständnisfrage zur Thread-Synchronisation

  Alt 11. Apr 2022, 08:09
Dazu ist ein klein wenig wichtig zu wissen, wie sich Windows verhält wenn man Threads startet.

Ein Thread der erzeugt wird wird, wenn man ihn Starten möchte, nicht sofort gestartet. Die Windows-API sagt dem Thread lediglich, dass er jetzt loslaufen kann, und der Windows-Scheduler kann ihn dann (muss ihn aber nicht sofort) beim nächsten mal wenn er arbeitet berücksichtigen und auf die CPU legen.

Das passiert frühestens(!) wenn der Haupt-Thread Deiner Anwendung yielded (das bedeutet: Rechenzeit abgibt). Warum das passiert (seine Zeitscheibe / Quantum ist abgelaufen, er blockiert wegen I/O, er gibt mit Sleep freiwillig Rechenzeit ab) ist dabei egal, aber der zweite Thread kommt eben frühestens das erste mal auf die CPU wenn der Hauptthread frei macht.

Auch danach kannst Du bei mehreren Threads nicht sicher sein, dass die wirklich Zeitgleich laufen. Es kann passieren, dass der Windows-Scheduler die Threads alle schön nacheinander ausführt.

Auch wichtig: Nur der UI-Thread kann das UI aktualisieren. Wenn der gerade nicht läuft wenn die anderen beiden Threads werkeln, bekommt das UI das nicht mit. Und da Du den im Prinzip mehr schlafen legst als das er Rechnet (Sleep-Aufrufe) kann der nicht viel machen.

Noch ein Wort zu den Quantums: Eine Zeitscheibe auf der CPU sind bei Windows aktuell vermutlich irgendwas zwischen 20 und 60 Millisekunden ( siehe auch: https://medium.com/@dikrek/processor...x-fb5ab02828e2 ). Nehmen wir mal Beispielhaft 30 an. Wenn der Thread jetzt z.B. nur eine Millisekunde rechnet und Du dann Sleep aufrufst, verwirfst Du den kompletten Rest Deiner Zeitscheibe. Das bedeutet der Scheduler nimmt den von der CPU runter und er kommt frühestens nach 29 Millisekunden wieder dran. Auch wenn Du nur 5ms Schlafen willst.

Will heissen: Mit Deinen extrem kurzen Cyclen (einfache Schleifen) und aus CPU-Sicht extrem vielen und langen Wartezeiten ärgerst Du gerade mehr den Windows-Scheduler als rauszufinden was wirklich passiert
Sebastian Gingter
Phoenix - 不死鳥, Microsoft MVP, Rettungshundeführer
Über mich: Sebastian Gingter @ Thinktecture Mein Blog: https://gingter.org
  Mit Zitat antworten Zitat
Benutzerbild von Phoenix
Phoenix
(Moderator)

Registriert seit: 25. Jun 2002
Ort: Hausach
7.641 Beiträge
 
#5

AW: Verständnisfrage zur Thread-Synchronisation

  Alt 11. Apr 2022, 08:14
4. Eine Exception Behandlung muss im Execute erfolgen.
Oh, auch hier vielleicht wichtig als Hintergrundinfo: Ein "Feature" von Windows ist es, bei einer unbehandelten(!) Exception in einem Sub-Thread immer gleich den kompletten Prozess zu terminieren. Und zwar sofort. Throw -> alle Fenster weg. Du kannst in dem Moment nicht mehr reagieren. Noch nichtmal loggen. Deswegen ist es sauwichtig das jeder von Dir erzeugte Thread entweder ganz sicher keine Exception werfen kann oder idealerweise die komplette Ausführung in einer try/catch - Anweisung läuft. Sonst: Plopp
Sebastian Gingter
Phoenix - 不死鳥, Microsoft MVP, Rettungshundeführer
Über mich: Sebastian Gingter @ Thinktecture Mein Blog: https://gingter.org
  Mit Zitat antworten Zitat
shebang

Registriert seit: 7. Feb 2020
131 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: Verständnisfrage zur Thread-Synchronisation

  Alt 11. Apr 2022, 10:32
Wenn der Thread jetzt z.B. nur eine Millisekunde rechnet und Du dann Sleep aufrufst, verwirfst Du den kompletten Rest Deiner Zeitscheibe. Das bedeutet der Scheduler nimmt den von der CPU runter und er kommt frühestens nach 29 Millisekunden wieder dran. Auch wenn Du nur 5ms Schlafen willst.
Auf aktuellen Multikernprozessoren stimmt das höchstens dann, wenn alle Kerne komplett ausgelastet sind und die Threads um Prozessorzeit konkurrieren. Ansonsten, wenn du einen Thread 5 ms schlafen legst, dann ist er nach 5-6 ms wieder wach und arbeitet weiter.
  Mit Zitat antworten Zitat
Benutzerbild von Stevie
Stevie

Registriert seit: 12. Aug 2003
Ort: Soest
4.027 Beiträge
 
Delphi 10.1 Berlin Enterprise
 
#7

AW: Verständnisfrage zur Thread-Synchronisation

  Alt 11. Apr 2022, 10:34
Oh, auch hier vielleicht wichtig als Hintergrundinfo: Ein "Feature" von Windows ist es, bei einer unbehandelten(!) Exception in einem Sub-Thread immer gleich den kompletten Prozess zu terminieren. Und zwar sofort. Throw -> alle Fenster weg. Du kannst in dem Moment nicht mehr reagieren. Noch nichtmal loggen. Deswegen ist es sauwichtig das jeder von Dir erzeugte Thread entweder ganz sicher keine Exception werfen kann oder idealerweise die komplette Ausführung in einer try/catch - Anweisung läuft. Sonst: Plopp
Wie gut, dass der Code in der RTL das schon übernimmt und jegliche Exception aus dem Thread Execute abfängt und an die Eigenschaft FatalException hängt, so dass das nicht passieren kann.
Stefan
“Simplicity, carried to the extreme, becomes elegance.” Jon Franklin

Delphi Sorcery - DSharp - Spring4D - TestInsight
  Mit Zitat antworten Zitat
EdAdvokat

Registriert seit: 1. Mai 2016
Ort: Berlin
419 Beiträge
 
Delphi 10.2 Tokyo Professional
 
#8

AW: Verständnisfrage zur Thread-Synchronisation

  Alt 11. Apr 2022, 11:54
zunächst erst einmal Dank für die konstruktiven Hinweise.
Doch so richtig weitergekommen bin ich noch nicht.
Gibt es denn nicht ein wirklich einfaches Beispiel für Hauptprogramm und ggf. 2 Threads, die synchronisiert werden ohne viel Schnick und Schnack.
Leider habe ich bisher nur mit div. Problemen überfrachtete Beispiele gefunden, die mir als Anfänger in dieser Sache nicht so richtig weiter helfen.
Den vorliegenden Code habe ich dahingehend verändert, dass nun im 2. Thread keine Schleife (bis 70) mehr enthalten ist sondern nur noch ein Activityindicator
mit anderem Aussehen. Also im Hauptprogramm wird eine Schleife bis 100 hoch gezählt und in den beiden Threads laufen die jeweils verschiedenen Activityindicatoren.
Diese laufen erkennbar beide gleichzeitig und wohl auch synchronisiert. Ist das also als Beispiel für ein Hauptprogramm mit 2 Threads tauglich?
Kennt jemand ein gutes Beispiel für meine Vorstellungen?
Norbert
  Mit Zitat antworten Zitat
Benutzerbild von KodeZwerg
KodeZwerg

Registriert seit: 1. Feb 2018
3.691 Beiträge
 
Delphi 11 Alexandria
 
#9

AW: Verständnisfrage zur Thread-Synchronisation

  Alt 11. Apr 2022, 18:21
ich weiß nicht ob es hilft aber so erstelle ich threads meist über die API, hier nur grob dargestellt
Delphi-Quellcode:
var
  ThreadHandle,
  ThreadId: TThreadID


procedure ThreadProc;
begin
  // mach was, auch GUI könnte man hier updaten etc...

  // thread wird beendet
  ExitThread(0);
end;

procedure foobar;
begin
  // einen thread wartend initialisieren
  ThreadHandle := CreateThread(nil, LongWord(0), @ThreadProc, nil, CREATE_SUSPENDED, ThreadId);
  // priorität festlegen
  SetThreadPriority(ThreadHandle, THREAD_PRIORITY_ABOVE_NORMAL);

  // thread starten
  ResumeThread(ThreadHandle);


  // einen thread abschießen
  if ((ThreadHandle <> 0) and (ThreadHandle <> INVALID_HANDLE_VALUE)) then
  begin
    TerminateThread(ThreadHandle, 0);
    WaitForSingleObject(ThreadHandle, 50);
    if ((ThreadHandle <> 0) and (ThreadHandle <> INVALID_HANDLE_VALUE)) then
      CloseHandle(ThreadHandle);
  end;
end;
Gruß vom KodeZwerg
  Mit Zitat antworten Zitat
Renate Schaaf

Registriert seit: 25. Jun 2020
Ort: Lippe
114 Beiträge
 
Delphi 11 Alexandria
 
#10

AW: Verständnisfrage zur Thread-Synchronisation

  Alt 11. Apr 2022, 19:31
There used to be an example in old versions of Delphi, which helped me to get started with threads. Apparently it is still available from here:
https://gist.github.com/jpluimers/8a...2ce7aaa8d4a2ea

Is ja die deutsche Seite . Also obiges Beispiel hat mir geholfen, als ich mit Threads angefangen habe.
Renate

Geändert von Renate Schaaf (11. Apr 2022 um 19:36 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 5  1 23     Letzte »    


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:06 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