Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Extra Thread starten (https://www.delphipraxis.net/210313-extra-thread-starten.html)

creehawk 5. Apr 2022 07:52

Extra Thread starten
 
Moin Moin!

Ich versuche mich gerade mal an der Programmierung eines Extra Threads. Mein Kunstwerk:

Delphi-Quellcode:
unit MainFormUnit;
interface
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.WinXCtrls, Vcl.ExtCtrls;

type
  TheThread = class(TThread)
  protected
    procedure Execute; override;
  end;

type
  TMainForm = class(TForm)
  Indicator: TActivityIndicator;
  CounterLabel: TLabel;   
  StartThreadBtn: TButton;
  procedure StartThreadBtnClick(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  MainForm: TMainForm;
  Thread: TheThread;
implementation

{$R *.dfm}

// *************************************************
procedure TheThread.Execute;
begin
  MainForm.Indicator.Animate := true;
end;
// *************************************************
procedure TMainForm.StartBtnClick(Sender: TObject);
begin
  try
   // Thread starten
        Thread := TheThread.Create(True);
    Thread.Resume;
        // eine reine Beschäftigungsaufgabe für den Hauptthread
   for I := 0 to 100 do
        begin
        MainForm.CounterLabel.Caption := I.tostring;
   Sleep(500);
        end;
        // Feierabend
   Thread.Terminate;
   Thread.FreeOnTerminate := True;
   MainForm.Indicator.Animate := false;
except
  on E:Exception do
  begin
     Thread.Terminate;
     Thread.FreeOnTerminate := True;
     MainForm.Indicator.Animate := false;
  end;
end;

end.
Es soll also während des durchlaufens eine Schleife ein Indicator animiert werden und nach Durchlauf wieder deanmiert werden. Passieren tut beim Start des Programms nichts. Ein Indicator erscheint nicht. Das Label bleibt bei 0.

Ich hatte das bis hier so verstanden, das ich den Hauptthread starte - also das Formular wird angezeigt, dann schmeisse ich per StartBtn einen weiteren Thread an, der den Animator anzeigt und diese Schleife durchläuft. Ist die durch ist Ende.

Tja, natürlich habe ich auch die Sychronize Sache gesehen, aber was soll ich hier sychronisieren?

Was mache ich falsch?

creehawk

sakura 5. Apr 2022 08:07

AW: Extra Thread starten
 
1. Deine "Arbeit" findet noch immer im Main-Thread statt und dieser blockiert die Nachrichtenschleife. Die Arbeit muss außerhalb des Main-Threads stattfinden.
2. Threads dürfen nicht unsynchronisiert auf die GUI zugreifen. Änderungen in der GUI müssen im Takt des Main-Threads stattfinden.

Hier im Forum suchensynchronize Hier im Forum suchentthread ...

...:cat:...

Sinspin 5. Apr 2022 08:15

AW: Extra Thread starten
 
Zitat:

Zitat von creehawk (Beitrag 1504251)
Tja, natürlich habe ich auch die Sychronize Sache gesehen, aber was soll ich hier sychronisieren?

Den Zugriff auf dein Fenster. Denn das dürfen Threads nicht einfach anfassen.

Einfach runtergeschrieben und nicht getestet, aber dafür vermutlich mehr wie es gedacht ist.

Delphi-Quellcode:
unit MainFormUnit;
interface
uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, 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
  TMainForm = class(TForm)
  Indicator: TActivityIndicator;
  CounterLabel: TLabel;   
  StartThreadBtn: TButton;
  procedure StartThreadBtnClick(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

// *************************************************
procedure TheThread.DoIt;
begin
  MainForm.Indicator.Animate := true;
end;

procedure TheThread.Execute;
begin
  Synchronize(DoIt);
end;
// *************************************************
procedure TMainForm.StartBtnClick(Sender: TObject);
var
  Thread: TheThread;
  I: integer;

begin
  try
    // Thread starten
    Thread := TheThread.Create(True);
    Thread.FreeOnTerminate := true;
    Thread.Resume;
    // eine reine Beschäftigungsaufgabe für den Hauptthread
    for I := 0 to 100 do
    begin
      MainForm.CounterLabel.Caption := I.tostring;
      Application.ProcessMessages;
      Sleep(500);
    end;
    MainForm.Indicator.Animate := false;
  except
    on E:Exception do
    begin
      MainForm.Indicator.Animate := false;
    end;
  end;
end.

creehawk 5. Apr 2022 08:40

AW: Extra Thread starten
 
Okay, also doch sychronisieren.

Allerdings läuft der Indicator jetzt nicht rund sondern ruckartig. Wird denn mit Synchronize die Animate Anweisung nicht immer wieder aufgrufen?

Ziel der ganzen Sache ist einfach einen Indicator anzuzeigen, während eine XML Datei erzeugt wird, was so 15 Sekunden dauert.

creehawk

jaenicke 5. Apr 2022 09:03

AW: Extra Thread starten
 
Der Fehler ist, dass du die Verarbeitung der XML-Datei im Hauptthread machst. Du musst das im Thread erledigen. Dann hast du im Hauptthread auch keine Blockade bei der Anzeige mehr.

peterbelow 5. Apr 2022 09:05

AW: Extra Thread starten
 
Zitat:

Zitat von creehawk (Beitrag 1504257)
Okay, also doch sychronisieren.

Allerdings läuft der Indicator jetzt nicht rund sondern ruckartig. Wird denn mit Synchronize die Animate Anweisung nicht immer wieder aufgrufen?

Ziel der ganzen Sache ist einfach einen Indicator anzuzeigen, während eine XML Datei erzeugt wird, was so 15 Sekunden dauert.

creehawk

Synchronize sendet eine Message an den main thread die bearbeitet wird, wenn der wieder in die message loop zurückkehrt. Das Sleep im main thread verhindert das. Du mußt die XML-Datei in einem Hintergrundthread erzeugen damit der main thread auch normal schnell messages verarbeiten kann.

Blup 5. Apr 2022 09:06

AW: Extra Thread starten
 
Dann nimm für die Zeitanzeige einen Timer.
Und es geht natürlich auch ohne Synchronize.
Delphi-Quellcode:
interface

uses
  Classes, SyncObjs,
  Forms, WinXCtrls, StdCtrls, ExtCtrls;

type
  TheThread = class(TThread)
    constructor Create(CreateSuspendet: Boolean);
    destructor Destroy; override;
  private
    FValue: Integer;
  protected
    FCS: TCriticalSection;
    procedure Execute; override;
    function GetValue: Integer;
    procedure SetValue(AValue: Integer);
  public
    property Value: Integer read GetValue write SetValue;
  end;

type
  TMainForm = class(TForm)
    Indicator: TActivityIndicator;
    CounterLabel: TLabel;
    StartThreadBtn: TButton;
    Timer: TTimer;
    procedure StartBtnClick(Sender: TObject);
    procedure DoOnTimer(Sender: TObject);
    procedure DoOnTerminate(Sender: TObject);
  private
    { Private-Deklarationen }
    FThread: TheThread;
  public
    { Public-Deklarationen }
  end;

var
  MainForm: TMainForm;

implementation

{$R *.dfm}

uses
  SysUtils;

// *************************************************
constructor TheThread.Create(CreateSuspendet: Boolean);
begin
  FCS := TCriticalSection.Create;
  inherited;
end;

destructor TheThread.Destroy;
begin
  inherited;
  FCS.Free;
end;

procedure TheThread.Execute;
var
  I: Integer;
begin
  try
    for I := 0 to 100 do
    begin
      Value := I; //  MainForm.CounterLabel.Caption
      Sleep(500);
    end;
  except
    // hier Fehlerbehandlung aller Fehler im Thread
  end;
end;

function TheThread.GetValue: Integer;
begin
  FCS.Enter;
  try
    Result := FValue;
  finally
    FCS.Release;
  end;
end;

procedure TheThread.SetValue(AValue: Integer);
begin
  FCS.Enter;
  try
    FValue := AValue;
  finally
    FCS.Release;
  end;
end;

// *************************************************
procedure TMainForm.StartBtnClick(Sender: TObject);
begin
  MainForm.Indicator.Animate := True;
  Timer.Enabled := True;
  // Thread starten
  FThread := TheThread.Create(True);
  FThread.FreeOnTerminate := True;
  FThread.OnTerminate := DoOnTerminate;
  FThread.Resume;
end;

procedure TMainForm.DoOnTimer(Sender: TObject);
begin
  if Assigned(FThread) then
  begin
    MainForm.CounterLabel.Caption := IntToStr(FThread.Value);
  end;
end;

procedure TMainForm.DoOnTerminate(Sender: TObject);
begin
  MainForm.Indicator.Animate := False;
  Timer.Enabled := False;
  FThread := nil;
end;

end.

creehawk 5. Apr 2022 09:26

AW: Extra Thread starten
 
Vielfältigen Dank für die Antworten!

Jetzt habe ich die Zusammenhänge (hoffentlich) verstanden. Auf in die Schlacht.

creehawk


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