Einzelnen Beitrag anzeigen

Ykcim

Registriert seit: 29. Dez 2006
Ort: NRW
804 Beiträge
 
Delphi 10.4 Sydney
 
#1

Erste Schritte Multi-Threading

  Alt 10. Okt 2023, 22:43
Hallo Zusammen,
ich habe eine Client/Server Applikation, in welcher die Server-App einige aufwändige Abfragen auf unterschiedlichen Datenbanken durchführt.
Diese Abfragen dauern ein paar Sekunden und wenn es unglücklich läuft, blockiert eine Abfrage eine andere. Das gibt dann eine Exception, die zwar abfgefangen werden, aber ich möchte das Ganze gerne in den Griff bekommen.

Daher wage ich mich erstmalig an das Thema Multi-Threading heran.

Ich habe eine ganz banale App geschrieben, in der ich die ersten Versuche bestritte habe und ich würde gerne mal ein Feedback von Euch hören, für die das Routine ist.

Delphi-Quellcode:
unit Frm_Main;

interface

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

type
  TForm1 = class(TForm)
    lbl_eins: TLabel;
    lbl_zwei: TLabel;
    btn_eins: TButton;
    btn_zwei: TButton;
    lbl_Zeit: TLabel;
    Timer1: TTimer;
    Timer_Eins_Start: TButton;
    Timer_Zwei_Start: TButton;
    Timer_Eins_Pause: TButton;
    Timer_Zwei_Pause: TButton;
    Timer_Eins_Resume: TButton;
    Timer_Zwei_Resume: TButton;
    Timer_Eins_Stop: TButton;
    Timer_Zwei_Stop: TButton;
    btn_EinsZwei: TButton;
    procedure btn_einsClick(Sender: TObject);
    procedure btn_zweiClick(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure Timer_Eins_StartClick(Sender: TObject);
    procedure Timer_Eins_ResumeClick(Sender: TObject);
    procedure Timer_Eins_StopClick(Sender: TObject);
    procedure Timer_Zwei_StartClick(Sender: TObject);
    procedure Timer_Eins_PauseClick(Sender: TObject);
    procedure Timer_Zwei_PauseClick(Sender: TObject);
    procedure Timer_Zwei_ResumeClick(Sender: TObject);
    procedure Timer_Zwei_StopClick(Sender: TObject);
    procedure btn_EinsZweiClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
    //Alles im Main-Thread
    procedure Timer_Eins;
    procedure Timer_Zwei;
    //Wird vom MyThread aufgerufen
    procedure Write_Counter_Eins(Counter_Eins: integer);
    procedure Write_Counter_Zwei (Counter_Zwei: integer);
    procedure Write_Uhr (Zeit: TTime);
  end;

TMyThread_Eins = class(TThread)
   public
      procedure Execute; override;
end;

TMyThread_Zwei = class(TThread)
   public
      procedure Execute; override;
end;

TMyThread_Uhr = class(TThread)
   public
      procedure Execute; override;
end;

var
  Form1: TForm1;
  TH_Eins: TMyThread_Eins;
  TH_Zwei: TMyThread_Zwei;
  TH_Uhr : TMyThread_Uhr;

implementation

{$R *.dfm}

procedure TForm1.btn_einsClick(Sender: TObject);
begin
   Timer_Eins;
end;

procedure TForm1.btn_zweiClick(Sender: TObject);
begin
   Timer_Zwei;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
   //TH_Eins.Free;
   //TH_Zwei.Free;
   TH_Uhr.Free;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
   TH_Uhr := TMyThread_Uhr.Create(False);
end;

procedure TForm1.btn_EinsZweiClick(Sender: TObject);
begin
   TH_Eins := TMyThread_Eins.Create(False);
   TH_Zwei := TMyThread_Zwei.Create(False);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
   lbl_Zeit.Caption := TimeToStr(now());
end;

procedure TForm1.Timer_Eins;
var I: integer;
      sEins: integer;
      c_Eins: integer;
begin
   sEins := 1000;
   c_Eins := 0;
   lbl_eins.Caption := IntToStr(c_Eins);
   lbl_eins.Refresh;
   for I := 0 to 9 do begin
      sleep(sEins);
      INC(c_Eins);
      lbl_eins.Caption := IntToStr(c_Eins);
      lbl_eins.Refresh;
   end;
end;

procedure TForm1.Timer_Zwei;
var I: integer;
      sZwei: integer;
      c_Zwei: integer;
begin
   sZwei := 1000;
   c_Zwei := 0;
   lbl_zwei.Caption := IntToStr(c_Zwei);
   lbl_zwei.Refresh;
   for I := 0 to 9 do begin
      sleep(sZwei);
      INC(c_Zwei);
      lbl_zwei.Caption := IntToStr(c_Zwei);
      lbl_zwei.Refresh;
   end;
end;

procedure TForm1.Timer_Eins_PauseClick(Sender: TObject);
begin
   if not TH_Eins.Terminated then begin
      TH_Eins.Suspend;
   end;
end;

procedure TForm1.Timer_Eins_ResumeClick(Sender: TObject);
begin
   if not TH_Eins.Terminated then begin
      TH_Eins.Resume;
   end;
end;

procedure TForm1.Timer_Eins_StartClick(Sender: TObject);
begin
   TH_Eins := TMyThread_Eins.Create(False);
end;

procedure TForm1.Timer_Eins_StopClick(Sender: TObject);
begin
   TH_Eins.Terminate;
end;

procedure TForm1.Timer_Zwei_PauseClick(Sender: TObject);
begin
   if not TH_Zwei.Terminated then begin
      TH_Zwei.Suspend;
   end;
end;

procedure TForm1.Timer_Zwei_ResumeClick(Sender: TObject);
begin
   if not TH_Zwei.Terminated then begin
      TH_Zwei.Resume;
   end;
end;

procedure TForm1.Timer_Zwei_StartClick(Sender: TObject);
begin
   TH_Zwei := TMyThread_Zwei.Create(False);
end;

procedure TForm1.Timer_Zwei_StopClick(Sender: TObject);
begin
   TH_Zwei.Terminate;
end;

procedure TForm1.Write_Counter_Eins(Counter_Eins: integer);
begin
   lbl_eins.Caption := IntToStr(Counter_Eins);
   lbl_eins.Refresh;
end;

procedure TForm1.Write_Counter_Zwei(Counter_Zwei: integer);
begin
   lbl_zwei.Caption := IntToStr(Counter_Zwei);
   lbl_zwei.Refresh;
end;

procedure TForm1.Write_Uhr(Zeit: TTime);
begin
   lbl_Zeit.Caption := TimeToStr(Zeit);
   lbl_Zeit.Refresh;
end;

{ TMyTread_Eins }

procedure TMyThread_Eins.Execute;
var I: integer;
      sEins: integer;
      c_Eins: integer;
begin
   sEins := 1000;
   c_Eins := 0;
   Synchronize(procedure
               begin
                  Form1.Write_Counter_Eins(c_Eins);
               end);
   for I := 0 to 19 do begin
      if Terminated then begin
         TH_Eins.Free;
         Break;
      end;
      sleep(sEins);
      INC(c_Eins);
      if Terminated then begin
         TH_Eins.Free;
         Break;
      end;
      Synchronize(procedure
                  begin
                     Form1.Write_Counter_Eins(c_Eins);
                  end);
   end;
end;

{ TMyThread_Zwei }

procedure TMyThread_Zwei.Execute;
var I: integer;
      sZwei: integer;
      c_Zwei: integer;
begin
   sZwei := 1000;
   c_Zwei := 0;
   Synchronize(procedure
               begin
                  Form1.Write_Counter_Zwei(c_Zwei);
               end);
   for I := 0 to 19 do begin
      if Terminated then begin
         TH_Zwei.Free;
         Break;
      end;
      sleep(sZwei);
      INC(c_Zwei);
      if Terminated then begin
         TH_Zwei.Free;
         Break;
      end;
      Synchronize(procedure
               begin
                  Form1.Write_Counter_Zwei(c_Zwei);
               end);
   end;
end;

{ TMyThread_Uhr }

procedure TMyThread_Uhr.Execute;
var I: integer;
      sUhr: integer;
begin
   sUhr := 1000;
   while not Terminated do begin
      sleep(sUhr);
      Synchronize(procedure
                  begin
                     Form1.Write_Uhr(Now());
                  end);
   end;

end;

end.
Dann habe ich auch noch zwei konkrete Frage: Wenn ich einen der beiden Threads durch erneutes Klicken des "Eins_Start" starte, während der Thread noch läuft, dass dann quasi ein weiterer Thread gestartet wird. Erkennbar, dass die Ausgabe zwischen den beiden Threads wechselt. Wie ist das möglich? Was für ein Thread wird denn da erzeugt? Ich habe doch mit einer globalen Variable gearbeitet?
Die zweite Frage bezieht sich auf Threads, die zur Laufzeit erzeugt werden: Wie kann ich einen Thread ansprechen, der zur Laufzeit erzeugt wurde? Ich habe das in dem kleinen Programm gelöst, indem ich mit globalen Variablen glöst habe. Aber ich habe keine Lösung gefunden, wie ich einen Thread, der zur Laufzeit ansprechen und z.B. pausieren oder stoppen kann.

Vielen Dank
Patrick
Angehängte Dateien
Dateityp: zip MultiThreading.zip (3,49 MB, 5x aufgerufen)
Patrick
  Mit Zitat antworten Zitat