Thread Synchronize (mit Parameter) im Jahr 2021

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
Nimral
Beiträge: 344
Registriert: Mi 10. Jun 2015, 11:33

Thread Synchronize (mit Parameter) im Jahr 2021

Beitrag von Nimral »

Mich holt gerade ein altes Stück Code ein :-)

Die Kurzform wäre: gibt es inzwischen eine Möglichkeit, einen Datenstruktur (hier: ein String) als Paramter von Synchronize von einem Thread in einen anderen Thread zu übergeben?

Ich hatte das letzte halbe Jahr mit c# zu tun, da gibt es neuerdings drei coole Sprachelemente (Anonyme Funktionen und die Möglichkeit, Variablen zu "capturen" plus Synchronize) die das Problem für mich sehr elegant lösbar machten, in etwa so:

Code: Alles auswählen

Synchronize(() => {Form1.Memo1.add("Bla")});
Ich meine, Delphi kann das inzwischen auch.

Mein alter Code, der gleich folgt, kann das nicht direkt, er funktioniert, aber ich finde, er ist im Vergleich recht umständlich.

Frage: alte Postings dass sowas mit FPC (noch?) nicht geht, und in Delphi aber schon (weil es dort anonyme Funktionen gibt) fand ich einige ... geht sowas inzwischen auch mit FPC?

Genug der Vorrede, hier mein (funktionierendes) Codebeispiel. Was mir nicht passt, ist natürlich der Umweg, die auszugebenden Daten erst mal in eine Threadvariable zu speichern, und die Thread-Objekte eng mit dem Memo in Form1 zu koppeln.

Code: Alles auswählen

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, RTTICtrls, ThreadUnit;

type

  { TForm1 }

  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    procedure HandleClick(aButton:TButton;var aThread:TTestThread);
  public

  end;

var
  Form1: TForm1;
  Thread1,Thread2,Thread3: TTestThread;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);

begin
  HandleClick(Button1,Thread1);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  HandleClick(Button2,Thread2);
end;

procedure TForm1.Button3Click(Sender: TObject);
begin
  HandleClick(Button3,Thread3);
end;

procedure TForm1.HandleClick(aButton: TButton; var aThread: TTestThread);

begin
  If not assigned(aThread) then // create thread
    begin
    aThread := TTestThread.Create(Memo1);
    aButton.Caption:=Format('Stop %d',[aThread.ThreadID]);
    aThread.Start;
    end
  else
    begin
    aThread.Terminate; // terminate thread
    while not aThread.isTerminated do  // wait for thread to terminate
      Application.ProcessMessages;
    FreeAndNil(aThread);
    aButton.Caption:='Start';
    end;
end;
end.
Und die Unit ThreadUnit (da wo sich das Meiste abspielt ...)

Code: Alles auswählen

unit ThreadUnit;

{$mode ObjFPC}{$H+}

interface

uses
  Classes, SysUtils, StdCtrls;

type

  { TTestThread }

  TTestThread = class(TThread)
    private
      FisTerminated: boolean;
      FMemo: TMemo;
      FThreadData: String;
      procedure SetMemo(AValue: TMemo);
      procedure SetThreadData(AValue: String);
    public
      property ThreadData:String read FThreadData write SetThreadData;
      property isTerminated: boolean read FisTerminated;
      procedure ShowData;
      Constructor Create(aMemo:TMemo);
      Procedure Execute; Override;
  end;

implementation

{ TTestThread }

procedure TTestThread.SetThreadData(AValue: String);
begin
  if FThreadData=AValue then Exit;
  FThreadData:=AValue;
end;

procedure TTestThread.SetMemo(AValue: TMemo);
begin
  if FMemo=AValue then Exit;
  FMemo:=AValue;
end;

procedure TTestThread.ShowData;

begin
  FMemo.Lines.add(Format('Thread %d says: %s',[Self.ThreadID,FThreadData]));
end;

constructor TTestThread.Create(aMemo: TMemo);
begin
  inherited create(true);
  self.FreeOnTerminate := false;
  FMemo := aMemo;
end;

procedure TTestThread.Execute;

var
  SleepTime:LongInt;

begin
  FisTerminated := false;
  FThreadData := 'Thread started';
  Synchronize(@ShowData);
  while not terminated do
    begin
      SleepTime := Random(4900) + 100;
      FThreadData := Format('I am alive! Going to sleep for %d ms',[SleepTime]);
      Synchronize(@ShowData);
      Self.Sleep(SleepTime);
    end;
  FThreadData := 'Thread stopped';
  Synchronize(@ShowData);
  FisTerminated := true;
end;

end.
Dateianhänge
unit1.zip
(107.49 KiB) 10-mal heruntergeladen

Benutzeravatar
theo
Beiträge: 8817
Registriert: Mo 11. Sep 2006, 19:01

Re: Thread Synchronize (mit Parameter) im Jahr 2021

Beitrag von theo »

Nimral hat geschrieben:
Mo 6. Dez 2021, 21:40
Genug der Vorrede, hier mein (funktionierendes) Codebeispiel. Was mir nicht passt, ist natürlich der Umweg, die auszugebenden Daten erst mal in eine Threadvariable zu speichern, und die Thread-Objekte eng mit dem Memo in Form1 zu koppeln.
Zu neuen Möglichkeiten der Sprache kann ich nichts sagen, aber das Memo muss ja (schon immer) nicht in die ThreadUnit rein. Es gibt doch Events.

Code: Alles auswählen

unit ThreadUnit;

{$mode ObjFPC}{$H+}

interface

uses
  Classes, SysUtils;

type

  TTestNotifyEvent = procedure(Sender: TObject; Mess: string) of object;

  { TTestThread }

  TTestThread = class(TThread)
  private
    FisTerminated: boolean;
    fOnTest: TTestNotifyEvent;
    FThreadData: string;
    procedure SetThreadData(AValue: string);
  public
    property ThreadData: string read FThreadData write SetThreadData;
    property isTerminated: boolean read FisTerminated;
    property OnTest: TTestNotifyEvent read fOnTest write fOnTest;
    procedure ShowData;
    constructor Create;
    procedure Execute; override;
  end;

implementation

{ TTestThread }

procedure TTestThread.SetThreadData(AValue: string);
begin
  if FThreadData = AValue then Exit;
  FThreadData := AValue;
end;

procedure TTestThread.ShowData;

begin
  if Assigned(fOnTest) then
    fOnTest(self, Format('Thread %d says: %s', [Self.ThreadID, FThreadData]));
end;

constructor TTestThread.Create;
begin
  inherited Create(True);
  self.FreeOnTerminate := False;
end;

procedure TTestThread.Execute;
var
  SleepTime: longint;
begin
  FisTerminated := False;
  FThreadData := 'Thread started';
  Synchronize(@ShowData);
  while not terminated do
  begin
    SleepTime := Random(4900) + 100;
    FThreadData := Format('I am alive! Going to sleep for %d ms', [SleepTime]);
    Synchronize(@ShowData);
    Self.Sleep(SleepTime);
  end;
  FThreadData := 'Thread stopped';
  Synchronize(@ShowData);
  FisTerminated := True;
end;

end.     

Code: Alles auswählen

procedure TForm1.ThreadTest(Sender: TObject; Mess: string);
begin
  Memo1.Lines.add(Mess);
end;

procedure TForm1.HandleClick(aButton: TButton; var aThread: TTestThread);
begin
  If not assigned(aThread) then // create thread
    begin
    aThread := TTestThread.Create;
    aThread.OnTest:=@ThreadTest;     

PascalDragon
Beiträge: 432
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Thread Synchronize (mit Parameter) im Jahr 2021

Beitrag von PascalDragon »

Nimral hat geschrieben:
Mo 6. Dez 2021, 21:40
Frage: alte Postings dass sowas mit FPC (noch?) nicht geht, und in Delphi aber schon (weil es dort anonyme Funktionen gibt) fand ich einige ... geht sowas inzwischen auch mit FPC?
Nein, anonyme Funktionen sind noch immer Work-In-Progress. Mein aktueller Plan ist das mit FPC 3.4.0 verfügbar zu haben (dem nächsten großen Release also)
FPC Compiler Entwickler

Nimral
Beiträge: 344
Registriert: Mi 10. Jun 2015, 11:33

Re: Thread Synchronize (mit Parameter) im Jahr 2021

Beitrag von Nimral »

Ich bin schon sehr gespannt, wie weit Du das Konzept treiben wirst :-)

Ich hatte anfangs schlimmste Probleme, den Code des längstgedienten C# Kollegen zu erfassen. Man kann mit Hilfe von Lambdas und Typeninferenz wohl sein ganzes Leben in einem Einzeiler ausdrücken. Der fand das alles super lesbar und übersichtlich, weils ja nur eine Zeile war.

Ich fands anfangs die Hölle, und das genaue Gegenteil von übersichtlich. Aber ich kann - nach 2 Monaten Einarbeitung - dem Konzept eine gewisse Eleganz nicht absprechen.

Armin.,

Nimral
Beiträge: 344
Registriert: Mi 10. Jun 2015, 11:33

Re: Thread Synchronize (mit Parameter) im Jahr 2021

Beitrag von Nimral »

theo hat geschrieben:
Di 7. Dez 2021, 10:40
Nimral hat geschrieben:
Mo 6. Dez 2021, 21:40
Genug der Vorrede, hier mein (funktionierendes) Codebeispiel. Was mir nicht passt, ist natürlich der Umweg, die auszugebenden Daten erst mal in eine Threadvariable zu speichern, und die Thread-Objekte eng mit dem Memo in Form1 zu koppeln.
Zu neuen Möglichkeiten der Sprache kann ich nichts sagen, aber das Memo muss ja (schon immer) nicht in die ThreadUnit rein. Es gibt doch Events.
Strimmt, so geht es besser.

Nennt man dieses Konstrukt eigentlich offiziell "Event"? Ich hab das bisher nur für Abkömmlinge von TEvent verwendet ...

Benutzeravatar
theo
Beiträge: 8817
Registriert: Mo 11. Sep 2006, 19:01

Re: Thread Synchronize (mit Parameter) im Jahr 2021

Beitrag von theo »

Nimral hat geschrieben:
Di 7. Dez 2021, 15:38
Nennt man dieses Konstrukt eigentlich offiziell "Event"? Ich hab das bisher nur für Abkömmlinge von TEvent verwendet ...
Diese Art des Programmierens nennt sich ja insgesamt "ereignisorientierte Programmierung".
Auf englisch ist das Ereignis ein "event".
https://de.wikipedia.org/wiki/Ereignis_(Programmierung)

S.a. https://www.freepascal.org/docs-html/rt ... event.html

Benutzeravatar
corpsman
Lazarusforum e. V.
Beiträge: 1258
Registriert: Sa 28. Feb 2009, 08:54
OS, Lazarus, FPC: Linux Mint Mate, Lazarus GIT Head, FPC 3.0
CPU-Target: 64Bit
Wohnort: Stuttgart
Kontaktdaten:

Re: Thread Synchronize (mit Parameter) im Jahr 2021

Beitrag von corpsman »

PascalDragon hat geschrieben:
Di 7. Dez 2021, 13:39
Nimral hat geschrieben:
Mo 6. Dez 2021, 21:40
Frage: alte Postings dass sowas mit FPC (noch?) nicht geht, und in Delphi aber schon (weil es dort anonyme Funktionen gibt) fand ich einige ... geht sowas inzwischen auch mit FPC?
Nein, anonyme Funktionen sind noch immer Work-In-Progress. Mein aktueller Plan ist das mit FPC 3.4.0 verfügbar zu haben (dem nächsten großen Release also)
Jehaa, da warte ich auch schon ein paar Jahre drauf, +1
--
Just try it

Antworten