AGB  ·  Datenschutz  ·  Impressum  







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

Allgemeine Fragen zu Threads

Ein Thema von norwegen60 · begonnen am 25. Jan 2017 · letzter Beitrag vom 25. Jan 2017
Antwort Antwort
Seite 1 von 2  1 2      
norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
505 Beiträge
 
Delphi 12 Athens
 
#1

Allgemeine Fragen zu Threads

  Alt 25. Jan 2017, 14:17
Delphi-Version: 10 Seattle
Hallo

ich habe mal ausgehend von dem Beispiel in http://docwiki.embarcadero.com/CodeE...dList_(Delphi) folgenden (sinnfreien) Code geschrieben. Ich weiß z.B. dass es besser wäre, in der Memo nur die fehlenden Werte zu ergänzen. Ich wollte den Code aber flach halten:
Delphi-Quellcode:
unit fo_ThreadListDePraxis;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Generics.Collections,
  Vcl.Samples.Spin;

type
  TForm1 = class(TForm)
    laSleeptime: TLabel;
    Memo1: TMemo;
    Button1: TButton;
    Button2: TButton;
    SpinEdit1: TSpinEdit;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button2Click(Sender: TObject);
    procedure SpinEdit1Change(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

  TMyList = class(TList<Integer>)
  end;

  TMyThread = class(TThread)
  private
    FCounter, FSleepTime: Integer;
  protected
    procedure Execute; override;
  public
    property SleepTime: Integer read FSleepTime write FSleepTime;
  end;

var
  Form1: TForm1;
  MyList: TMyList;
  MyThread: TMyThread;

implementation

{$R *.dfm}

procedure TForm1.Button2Click(Sender: TObject);
begin
  if assigned(MyThread) then
  begin
    MyThread.Terminate; // Setzt MyThread nicht auf NIL, d.h. nächster Button2Click führt wieder in diesn Block
    Button2.Caption := 'Start Thread';
  end
  else
  begin
    Button2.Caption := 'Terminate Thread';
    MyThread := TMyThread.Create(true); // Create suspended--secondProcess does not run yet.
    MyThread.FreeOnTerminate := true; // You do not need to clean up after termination.
    MyThread.SleepTime := SpinEdit1.Value;
    MyThread.Resume; // Now run the thread.
  end;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  MyList.Free;
  MyThread.Terminate;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  MyList := TMyList.Create;
end;

procedure TForm1.SpinEdit1Change(Sender: TObject);
begin
  if assigned(MyThread) then
    MyThread.SleepTime := SpinEdit1.Value;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  i: Integer;
begin
  for i := 0 to 20 do
    MyList.Add(random(200));
end;

{ TMyThread }

procedure TMyThread.Execute;
var
  i: Integer;
begin
  inherited;

  while (true) do
  begin
    if Terminated then
      exit;

    inc(FCounter);
    Form1.Label1.Caption := IntToStr(FCounter);
    Form1.Memo1.Clear;

    System.TMonitor.Enter(MyList); // Liste gegen Zweitzugriff sperren. Muss das sein?
    try
      for i := 0 to MyList.Count - 1 do
      begin
        if Terminated then
          exit;
        Form1.Memo1.Lines.Add(format('%d: %d', [i, MyList.Items[i]])); // Ist das zulässig?
      end;
    finally
      System.TMonitor.exit(MyList); // Sperrung der Liste wieder aufheben
    end;
    sleep(FSleepTime); // FSleepTime oder besser SleepTime?
  end;
end;

end.
Meine Fragen:
  1. Überall heißt es man soll aus Threads nicht direkt auf Form-Elemente zugreifen. Hier wird es doch gemacht. Also doch kein Problem wenn sicher ist dass kein anderer auf Form1.Memo1 zugreift? Und abgesehen davon dass es sauberer ist die Trennung per Synchronize zu machen?
  2. Im Execute greife ich auf die globale Liste MyList zu. Muss ich die zwingend mit System.TMonitor.Enter sperren wenn ich nur lesen will?
    • Was kann passieren wenn ich nur Werte hinzufüge?
    • Was wenn sich auch Werte ändern könnten?
  3. Obwohl ich MyThread.FreeOnTerminate := true; gesetzt habe funktioniert die Assign-Abfrage in procedure TForm1.Button2Click(Sender: TObject) . Setze ich einen Haltepunkt nach dem Terminate ist MyThread nicht NIL.
  4. Die Anwendung wirft in der IDE eine Exception wenn in FormClose MyThread.Terminate; nicht aufgerufen wird. Muss ich beim Beenden sonst noch was beachten?
  5. Greift man innerhalb einer Klasse besser auf die Variable oder das Property zu (FSleepTime oder SleeptTime)?

Danke für euer Feedback
Gerd

Geändert von TBx (25. Jan 2017 um 15:41 Uhr) Grund: Habe dem Threadtitel ein r spendiert
  Mit Zitat antworten Zitat
a.def
(Gast)

n/a Beiträge
 
#2

AW: Allgemeine Fragen zu Theads

  Alt 25. Jan 2017, 14:32
Soweit ich (nun) weiß müssen lesende als auch schreibende Zugriffe geschützt werden.
Statt TMonitor würde ich aber CriticalSections benutzen. Die Ausführung ist weitaus schneller.

Hier wird ja nur die Liste geschützt. Das Memo aber nicht. Das Emba-Beispiel ist keines, welches man sich "näher" ansehen sollte finde ich.
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe
Online

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.443 Beiträge
 
Delphi 12 Athens
 
#3

AW: Allgemeine Fragen zu Theads

  Alt 25. Jan 2017, 14:35
Überall heißt es man soll aus Threads nicht direkt auf Form-Elemente zugreifen. Hier wird es doch gemacht. Also doch kein Problem wenn sicher ist dass kein anderer auf Form1.Memo1 zugreift?
Das ist so nicht richtig. Korrekt müsste es heißen: "wenn sicher ist, dass kein anderer auf die VCL zugreift", aber das kannst du einfach nicht sicherstellen, wenn das nicht im Hauptthread abläuft. Hintergrund: die VCL kapselt die Winapi-Zugriffe und legt dafür dynamisch die benötigten Handles an, sobald sie gebraucht werden. Da es sich hierbei auch schon mal um globale Variablen oder Objekte handelt, ist eine Verwendung in getrennten Threads potentiell tödlich für die Anwendung. Deswegen die Maßgabe: alle Zugriffe auf die VCL ausschließlich aus dem Hauptthread heraus!

Im Execute greife ich auf die globale Liste MyList zu. Muss ich die zwingend mit System.TMonitor.Enter sperren wenn ich nur lesen will?
Solange die Liste überhaupt nicht verändert wird, sollte das Lesen funktionieren. Sobald aber irgendwer eventuell eine Änderung machen könnte, ist das nicht mehr der Fall.

Was kann passieren wenn ich nur Werte hinzufüge?
Sobald dabei mehrere Threads beteiligt sind (Lesen und/oder Schreiben) muss synchronisiert werden. Beim Schreiben kann es vorkommen, daß der der Speicher neu alloziert wird. Davon wäre auch ein Lesen betroffen.

Was wenn sich auch Werte ändern könnten?
Da auch hier keine atomare Operation angenommen werden kann, ist ein Synchronisieren notwendig.

Obwohl ich MyThread.FreeOnTerminate := true; gesetzt habe funktioniert die Assign-Abfrage in procedure TForm1.Button2Click(Sender: TObject) . Setze ich einen Haltepunkt nach dem Terminate ist MyThread nicht NIL.
Das Free gibt ja auch nur die Instanz frei, setzt aber deine Variable nicht auf nil.

Die Anwendung wirft in der IDE eine Exception wenn in FormClose MyThread.Terminate; nicht aufgerufen wird. Muss ich beim Beenden sonst noch was beachten?
Siehe oben.

Greift man innerhalb einer Klasse besser auf die Variable oder das Property zu (FSleepTime oder SleeptTime)?
Das ist in diesem Fall Geschmackssache.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Benutzerbild von sakura
sakura

Registriert seit: 10. Jun 2002
Ort: Unterhaching
11.412 Beiträge
 
Delphi 12 Athens
 
#4

AW: Allgemeine Fragen zu Theads

  Alt 25. Jan 2017, 14:40
[*]Überall heißt es man soll aus Threads nicht direkt auf Form-Elemente zugreifen. Hier wird es doch gemacht. Also doch kein Problem wenn sicher ist dass kein anderer auf Form1.Memo1 zugreift? Und abgesehen davon dass es sauberer ist die Trennung per Synchronize zu machen?
Nur weil es gemacht wird, ist es nicht korrekt. Die Beispiele sind oft von Testern aus der Beta-Phase, nicht vom Embarcadero selbst.
[*]Im Execute greife ich auf die globale Liste MyList zu. Muss ich die zwingend mit System.TMonitor.Enter sperren wenn ich nur lesen will?
Es gibt, wie oben schon beschrieben auch andere Möglichkeiten, aber eine dieser solltest tue nutzen.
[*]Was kann passieren wenn ich nur Werte hinzufüge?
Zugriffsverletzungen, invalide Daten, Nichts... je nach dem, was andere Threads gerade machen
[*]Was wenn sich auch Werte ändern könnten?
^^
[*]Obwohl ich MyThread.FreeOnTerminate := true; gesetzt habe funktioniert die Assign-Abfrage in procedure TForm1.Button2Click(Sender: TObject) . Setze ich einen Haltepunkt nach dem Terminate ist MyThread nicht NIL.
Free gibt das Objekt frei, die Variabeln werden aber nicht beeinflusst.
[*]Greift man innerhalb einer Klasse besser auf die Variable oder das Property zu (FSleepTime oder SleeptTime)?
SleepTime - dann lässt sich die Implementierung ändern, ohne dass Du überall im Quellcode nachschauen musst.

......
Daniel Lizbeth
Ich bin nicht zurück, ich tue nur so
  Mit Zitat antworten Zitat
a.def
(Gast)

n/a Beiträge
 
#5

AW: Allgemeine Fragen zu Theads

  Alt 25. Jan 2017, 14:41
Wenn die Profis schon sprechen (ihr):
was haltet ihr denn von TMonitor und CriticalSections? Welches bevorzugt ihr? Soweit ich weiß hat CriticalSections weniger Overhead und ist deutlich schneller als TMonitor.

Wenn ich darf habe ich auch eine grundlegende Frage zur Synchronisation:
wenn man zwei Threads hat, beide greifen auf die VLC zu (Memo.Lines.Add) und man das synchronisiert, verliert man dann nicht den Vorteil den mehrere Threads eigentlich bieten sollten?

Geändert von a.def (25. Jan 2017 um 14:46 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von sakura
sakura

Registriert seit: 10. Jun 2002
Ort: Unterhaching
11.412 Beiträge
 
Delphi 12 Athens
 
#6

AW: Allgemeine Fragen zu Theads

  Alt 25. Jan 2017, 14:46
TMonitor und CriticalSections? Welches bevorzugst du?
Alle haben ihre Vor- und Nachteile und es gibt noch mehr. Das hängt wirklich davon ab, was Du erreichen willst. Hier hilft nur studieren und die entsprechende Situation analysieren. Zum Über ist TCriticalSection aber ein guter Anfang. Dann gibt es auch noch TMultiReadExclusiveWriteSynchronizer, TSpinLock, TSpinWait, TSemaphore, TMutex, und, und, und...

......
Daniel Lizbeth
Ich bin nicht zurück, ich tue nur so
  Mit Zitat antworten Zitat
Benutzerbild von sakura
sakura

Registriert seit: 10. Jun 2002
Ort: Unterhaching
11.412 Beiträge
 
Delphi 12 Athens
 
#7

AW: Allgemeine Fragen zu Theads

  Alt 25. Jan 2017, 14:48
wenn man zwei Threads hat, beide greifen auf die VLC zu (Memo.Lines.Add) und man das synchronisiert, verliert man dann nicht den Vorteil den mehrere Threads eigentlich bieten sollten?
Gut möglich, es kommt auch darauf an, wie lange die eigentliche Arbeit im Vergleich zur Synchronisation braucht. Grundsätzlich solltest Du versuchen auf Synchronisation mit der VCL zu verzichten und die Daten anderweitig zur Verfügung zu stellen und diese dann in der VCL-Anwendung abfragen.

......
Daniel Lizbeth
Ich bin nicht zurück, ich tue nur so
  Mit Zitat antworten Zitat
HolgerX

Registriert seit: 10. Apr 2006
Ort: Leverkusen
969 Beiträge
 
Delphi 6 Professional
 
#8

AW: Allgemeine Fragen zu Theads

  Alt 25. Jan 2017, 14:54
Hmmm..

Damit deine Thread-Variable auf nil gesetzt wird, brauchst Du so etwas:

( Und wieso die Globale Variable 'MyThread', obwohl nur innerhalb der Form darauf zugegriffen wird !!)
Delphi-Quellcode:
  TForm1 = class(TForm)
    ...
    procedure Button2Click(Sender: TObject);
  private
    procedure OnMyThreadTerminate(Sender: TObject);
  protected
    MyThread : TMyThread;
  public
    { Public-Deklarationen }
  end;


procedure TForm1.Button2Click(Sender: TObject);
begin
  if assigned(MyThread) then
  begin
    MyThread.Terminate; // Setzt MyThread nicht auf NIL, d.h. nächster Button2Click führt wieder in diesn Block
    Button2.Enabled := false; // Erst benutzbar machen, wenn Thread wirklich weg!
  end
  else
  begin
    Button2.Caption := 'Terminate Thread';
    MyThread := TMyThread.Create(true); // Create suspended--secondProcess does not run yet.
    MyThread.FreeOnTerminate := true; // You do not need to clean up after termination.

    MyThread.OnTerminate := OnMyThreadTerminate; // Damit die Form benachrichtigt wird !!!!

    MyThread.SleepTime := SpinEdit1.Value;
    MyThread.Resume; // Now run the thread.
  end;
end;

procedure TForm1.OnMyThreadTerminate(Sender: TObject);
begin
  if Sender = MyThread then begin
    MyThread := nil; // Nur nil, Free macht er selber!
    Button2.Caption := 'Start Thread';
    Button2.Enabled := true; // Erst benutzbar machen, wenn Thread wirklich weg!
  end;
end;
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe
Online

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.443 Beiträge
 
Delphi 12 Athens
 
#9

AW: Allgemeine Fragen zu Theads

  Alt 25. Jan 2017, 14:57
was haltet ihr denn von TMonitor und CriticalSections? Welches bevorzugt ihr? Soweit ich weiß hat CriticalSections weniger Overhead und ist deutlich schneller als TMonitor.
Das sollte seit XE5 kein Problem mehr sein.

Wenn ich darf habe ich auch eine grundlegende Frage zur Synchronisation:
wenn man zwei Threads hat, beide greifen auf die VLC zu (Memo.Lines.Add) und man das synchronisiert, verliert man dann nicht den Vorteil den mehrere Threads eigentlich bieten sollten?
Wenn die Hauptaufgabe in dem synchronisierten Part steckt, ja. Wenn sich das auch nicht ändern lässt, dann hat man wenig Chancen und kann vermutlich ganz auf die Threads verzichten. Geht es darum, eine längere Aktion nur im VCL-Thread auszuführen, ohne die UI zu blocken, lässt sich das auch durch einen StateMachine-Ansatz lösen.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
norwegen60

Registriert seit: 23. Dez 2007
Ort: Schwarzwald
505 Beiträge
 
Delphi 12 Athens
 
#10

AW: Allgemeine Fragen zu Theads

  Alt 25. Jan 2017, 15:39
Ihr seit einfach super.

Korrekt müsste es heißen: "wenn sicher ist, dass kein anderer auf die VCL zugreift", ...
Ja, so dachte ich mir das.

Das Free gibt ja auch nur die Instanz frei, setzt aber deine Variable nicht auf nil.
Ja, klar, heißt ja nicht FreeAndNil

Soweit ich (nun) weiß müssen lesende als auch schreibende Zugriffe geschützt werden.
Statt TMonitor würde ich aber CriticalSections benutzen. Die Ausführung ist weitaus schneller.
Das widerspricht sich jetzt mit
was haltet ihr denn von TMonitor und CriticalSections? Welches bevorzugt ihr? Soweit ich weiß hat CriticalSections weniger Overhead und ist deutlich schneller als TMonitor.
Das sollte seit XE5 kein Problem mehr sein.
Irgendwann schau ich mr auch mal die Themen CriticalSection, TMultiReadExclusiveWriteSynchronizer, TSpinLock, TSpinWait, TSemaphore, TMutex, und, und, und... an, aber im Moment muss ich es erst mal setzen lassen und etwas rum probieren.

Danke
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


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 13:33 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