Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Multithreading GUI (https://www.delphipraxis.net/178798-multithreading-gui.html)

value is NULL 28. Jan 2014 13:01

Multithreading GUI
 
Hallo liebe Community!

Kaum ist das eine Problem gelöst, schon taucht das nächste auf.
Und zwar bin ich auf der Suche nach einem guten Multithreading Tutorial.

Gefunden habe ich schon einiges, will jedoch TThread verwenden.

Die Aufgabe:
Ich habe eine Formularanwendung auf der sich ein Memo befindet sowie ein Start Button. Wenn ich auf Start klicke,
werden verschiedenste Funktionen und Prozeduren aufgerufen. Diese Prozeduren will ich jetzt als Thread laufen lassen.

Ich hätte mir das ungefähr so gedacht:

Delphi-Quellcode:
unit Unit1;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Dialogs, FMX.Ani, FMX.Layouts, FMX.Gestures,
  FMX.Memo, FMX.StdCtrls;

type
  TForm1 = class(TForm)
    StyleBook1: TStyleBook;
    Memo1: TMemo;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

type
  TMyOwnThread = class(TThread)
  private
     { Private-Deklarationen }
  protected
    procedure Execute; override;
  public
    { Public-Deklarationen }
  end;

  procedure test;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure test;
var
  i : Integer;
begin
  i := 0;
  while i <= 20 do begin
     Form1.Memo1.Lines.Add(IntTostr(i));
    i := i + 1;
  end;
end;

procedure TMyOwnThread.Execute;
begin
  try
    Synchronize(test);
  except
    on e: exception do begin
      Showmessage(e.Message);
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  thread : TMyOwnThread;
begin
  thread := TMyOwnThread.Create(False);
  thread.WaitFor;
  thread.Free;
end;

end.
Prinzipiell mal die Frage: Wäre das so in ungefähr korrekt?
und 2. gibt es die Möglichkeit eine Art "Write" funktion zu schreiben wie zB:

Delphi-Quellcode:
function write(msg : string) : String;
begin
  Form1.Memo1.Lines.Add(msg);
end;
und diese aus dem Thread "Synchron" aufzurufen?

Vielen Dank im Voraus und sorry falls ich mich gerade blöd anstelle aber will hier nichts falsch machen ^^

LG

taveuni 28. Jan 2014 13:30

AW: Multithreading GUI
 
Was hast Du für eine Delphi Version?
Könntest Du die unter Umständen freundlicherweise eintragen?
Abhängig davon fallen die Antworten unterschiedlich aus.

himitsu 28. Jan 2014 13:34

AW: Multithreading GUI
 
Wenn du die meiste Zeit synchronisiert in der GUI rumhängst, dan bringt der Thread garnichts und ist eher kontraproduktiv.

Du kannst aber eine TStringList in den Thread nehmen, da alles einfügen und am Ende alles Gemeinsam via AddStrings ins Memo einfügen. (oder zumindestens grüppchenweise weniger oft übergeben)

Du kannst in dem Thread alles machen, aber sobald "globale" nicht threadsichere dinge genutzt werden, mußt du synchronisieren oder sonstwie die Zugriffe regeln.

Ach ja, wenn du zu oft Synchronize aufrufst, also vorallem viele Male extrem schnell hinterinander, dann kannst du damit den GUI-Thread lahmlegen, da er keiner Zeit mehr für seinen eigenen Kram hat.




Im Delphi find ich einmalig genutzte und "kurze" Sync-Methoden via Generics, genauer als anonyme Methode oftmals irgendwie übersichtlicher.
Vorallem da man dort Parameter Variablen übergeben bekommt, ohne daß Diese global sein müssen. (was Probleme gibt, wenn die Methode+Variable von mehreren Threads aus gleichzeigt aufgerufen werden kann)
Delphi-Quellcode:
procedure TMyOwnThread.Execute;
begin
  try
    Synchronize(nil, procedure
      var
        i : Integer;
      begin
        i := 0;
        while i <= 20 do begin
           Form1.Memo1.Lines.Add(IntTostr(i));
          i := i + 1;
        end;
      end);
  except
    on e: Exception do
      ShowMessage(e.Message);
  end;
end;
So ginge es auch.
Delphi-Quellcode:
procedure TMyOwnThread.Execute;
var
  i : Integer;
begin
  try
    i := 0;
    Synchronize(nil, procedure
      begin
        while i <= 20 do begin
          Form1.Memo1.Lines.Add(IntTostr(i));
          i := i + 1;
        end;
      end);
  except
    on e: Exception do
      ShowMessage(e.Message);
  end;
end;

himitsu 28. Jan 2014 13:35

AW: Multithreading GUI
 
Zitat:

Zitat von taveuni (Beitrag 1245667)
Was hast Du für eine Delphi Version?

Schlecht wäre es nicht.

Zitat:

Delphi-Quellcode:
{$R *.fmx}

Aber sooooo alt ist Seine vermutlich nicht.

und Diesbezüglich hat sich, in letzter Zeit, eigentlich nicht viel geändert.

[edit]
http://www.delphipraxis.net/178791-t...iremonkey.html -> siehe Ausgangsfrage, letzte Zeile

Der schöne Günther 28. Jan 2014 13:42

AW: Multithreading GUI
 
Jetzt haben mich doch einige überholt. Hier trotzdem der Text:


Warum baumelt die Methode "test" da so einsam herum? Das gehört doch zu deinem
Delphi-Quellcode:
TMyOwnThread
. Also

Delphi-Quellcode:
TMyOwnThread = class(TThread)
   protected
      procedure test();
      procedure Execute(); override;
end;

bzw.

TMyOwnThread = class(TThread)
   private
      procedure test();
   protected
      procedure Execute(); override;
end;

Wenn du einen Thread nicht irgendwo manuell freigeben willst, kannst du auf deiner Threadvariable auch
Delphi-Quellcode:
FreeOnTerminate := True
setzen, das kann manchmal sehr praktisch sein :-) - Der gibt sich dann von alleine wieder frei.

Warum wartest du im OnClick-Handler auf den Thread? Das führt doch das Konzept eines Thread ad absurdum. Es soll doch gerade nicht die Oberfläche blockiert werden, sondern etwas im Hintergrund geschehen. Oder war das gerade nur, weil du ihn ordentlich wieder freigeben wolltest, wenn er zu Ende ist?

Letztendlich: Zum testen sicher gut, aber bitte gewöhn dir für die Praxis besser nicht an, von einem Thread aus etwas in die Formulare "hineinzu-synchronisieren". Der Thread ist für Hintergrund-Arbeiten da.

Weiterhin: Ich finde den Exception-Handler im Thread eher unsinnig. Ich habe noch nicht ganz verstanden, welches Schreckensszenario himitsu eben an die Wand gemalt hat, aber wenn in einem Thread eine Exception ganz nach oben bubbelt dann wird er beendet. Aber doch bitte nicht die ganze Anwendung?


PS: Ein VCL-ShowMessage erstellt ein Formular. In einem Thread keine gute Sache. Bei Firemonkey blicke ich spontan nicht durch. Kann man das in einem Thread überhaupt machen?

PPS:
Delphi-Quellcode:
TThread.CreateAnonymousThread(..)
ist an sich eine tolle Sache. Sonderlich viel leserlicher finde ich das nicht, insbesondere wenn es umfangreich wird. Für kleine Dinge aber eine Tolle Sache.

value is NULL 28. Jan 2014 13:50

AW: Multithreading GUI
 
Danke für die Antworten!
Die verwendete Delphi Version ist Delphi XE5 Architect.

Delphi-Quellcode:
procedure TMyOwnThread.Execute;
var
  i : Integer;
begin
  try
    i := 0;
    Synchronize(nil, procedure
      begin
        while i <= 20 do begin
          Form1.Memo1.Lines.Add(IntTostr(i));
          i := i + 1;
        end;
      end);
  except
    on e: Exception do
      ShowMessage(e.Message);
  end;
end;
Das hier klingt interessant. Ist es von Nachteil in so einer Procedure mehr zu verknüfpen? Plan wäre zB folgender:

Delphi-Quellcode:
procedure TMyOwnThread.Execute;
var
  i : Integer;
begin
   try
    i := 0;
    Synchronize(nil, procedure
      begin
        if Form1.CheckBox1.IsChecked then procedure1;
        if Form1.CheckBox2.IsChecked then procedure2;
        if Form1.CheckBox3.IsChecked then procedure3;
        if Form1.CheckBox4.IsChecked then procedure4;
        if Form1.CheckBox5.IsChecked then procedure5;
        if Form1.CheckBox6.IsChecked then procedure6;
      end);
  except
    on e: Exception do
      ShowMessage(e.Message);
  end;
end;
wobei es sich bei procedure1 zB um folgendes handelt:

Delphi-Quellcode:
procedure procedure1();
begin
  Form1.Memo1.Lines.Add('procedure 1 gestartet');
  //irgendwelche dinge
  Form1.Memo1.Lines.Add('procedure 1 beendet');
end;
--------------------------------------------------------------
Was ich eigentlich zu erreichen versuche, vermutlich habe ich das nicht richtig rüber gebracht,
Der User wählt diverse Dinge aus per checkbox und dementsprechend werden dann verschiedene Funktionen aufgerufen die ihre arbeit verrichten.
Jede Funktion schreibt diverse Infos in ein Memo (ich taufe dies jetzt mal "LiveLog")

Das heisst ich will natürlich:

A) Das GUI nicht zum "freeze" bringen
und B) von jeder procedure dann auf die Komponenten wie Checkboxen, TEdit's, Memo zugreifen

stahli 28. Jan 2014 14:12

AW: Multithreading GUI
 
Ein schönes Tutorial zu Threads gibt es von Luckie:
http://www.michael-puff.de/Programmi...phi/Tutorials/

Ich spiele auch gerade mit Threads und GUI (aber nicht VCL und FMX) herum.
Man darf grundsätzlich nicht schreibend aus zwei Threads auf einen Speicherplatz zugreifen.

Entweder kann man undefinierte Ergebnisse erhalten oder direkt Zugriffsfehler.
Das kann man sich gut an einer Liste verdeutlichen.
Wenn ein Thread gerade einen neuen Eintrag anhängen will und ein anderer zum gleichen Zeitpunkt den ersten Eintrag entfernt wird das zu einem Fehler führen.

Das selbe Problem besteht bei Zugriffen auf die GUI-Controls.
Um diese Probleme zu vermeiden gibt es CriticalSections. Thread1 "sperrt den Speicherplatz oder eine Komponente" bis er fertig ist mit seiner Aufgabe. Solange muss Thread2 warten und kann seine Aufgabe erst danach erledigen.

Syncronisize kapselt intern eine CriticalSection und vereinfacht so deren Verwendung (geht aber nur bei der Syncronisierung mit dem Mainthread).

Bei langen Berechnungen muss man überlegen, wie oft man Syncronisize aufruft, da das den Berechnungsthread bremst (er muss warten bis der Mainthread fertig ist). In Bezug auf die VCL oder FMX wäre ich nicht sicher, ob sich dabei alle Probleme vermeiden lassen.


Warum verwendest Du überhaupt Threads? Soll der Anwender zwischendurch weiter arbeiten können? Nur dann machen Threads wirklich Sinn.
Andernfalls könntest Du Deine Schalter deaktivieren und in Deinen Funktionen gelegentlich Application.Processmessages aufrufen.
Dann würde man die Einträge im Memo sehen und das Formular optisch noch etwas tun.

Sir Rufo 28. Jan 2014 14:12

AW: Multithreading GUI
 
Nein, so macht man das nie, niemals, nicht.

Wenn der Thread startet, dann greift der nicht mehr auf die Form zu.
Und schon gar nicht über die globale Form-Variable.

Das knallt sonst schneller als du denkst.

Und wenn du alles, was du ausführen möchtest synchronisiert ausführst, wofür dann erst einen Thread?

value is NULL 28. Jan 2014 14:17

AW: Multithreading GUI
 
Prinzipiell soll er mit der GUI eigentlich nichts mehr machen können.
Das einzige was halt "schön" wäre.. wenn sich das Memo wie ein "liveLog" verhält.

Sprich: es wird eine Funktion aufgerufen die bestimmte dinge erledigt und schreibt das ins memo
Dann wird funktion2 aufgerufen und schreibt seinen Status in das Memo
usw usw ...

Der User soll halt sehen, das sich da was tut. Jetzt ist es so, das nach dem Durchlauf das Memo vollgeschrieben wird, sprich wenn schon alle Funktionen fertig sind :/
Deswegen habe ich an Threads gedacht

himitsu 28. Jan 2014 14:44

AW: Multithreading GUI
 
Zitat:

Delphi-Quellcode:
procedure procedure1();
begin
  Form1.Memo1.Lines.Add('procedure 1 gestartet');
  //irgendwelche dinge
  Form1.Memo1.Lines.Add('procedure 1 beendet');
end;

Was macht denn "irgendwelche dinge"?

Wenn da nichts mit der GUI oder was anderem Globalen gemacht wird, dann solltest du nur die beiden Lines.Add synchronisieren und nicht die ganze Prozedur.
So würde das "irgendwelche dinge" im Thread laufen und nicht die GUI blockieren.


Alle Zeitangaben in WEZ +1. Es ist jetzt 15:04 Uhr.
Seite 1 von 2  1 2      

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