AGB  ·  Datenschutz  ·  Impressum  







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

Update-Vorgang in einen Thread auslagern

Ein Thema von mm1256 · begonnen am 18. Dez 2014 · letzter Beitrag vom 22. Dez 2014
Antwort Antwort
Seite 2 von 4     12 34      
Dejan Vu
(Gast)

n/a Beiträge
 
#11

AW: Update-Vorgang in einen Thread auslagern

  Alt 19. Dez 2014, 08:32
Die Kommunikation zwischen Threads und der Außenwelt geschieht über synchronisierte Events und geschützte Properties.

Der Thread ackert also im Hintergrund, möchte aber, das sein innerer Zustand (Was macht er gerade? Wie weit ist er?) in einer Form sichtbar ist.

Fein. Dann unterhalten die sich eben, aber nicht direkt, bitte. Das ist praktisch, weil so eine Form auch gut für andere Threads verwendet werden kann und andererseits so ein Thread auch mit einer anderen Form reden kann (oder mit gar keiner, sollte ihm schnurz sein).

Es bieten sich zum Unterhalten Events oder Interfaces an. Beides muss aber synchronisiert werden, d.h. Thread und Außenwelt laufen ja in unterschiedlichen Threads und die VCL ist nicht threadsafe, d.h. alles, was mit der VCL geschieht, muss im Hauptthread gemacht werden.

Nehmen wir mal folgenden Thread;
Delphi-Quellcode:
Procedure TMyThread.Execute;
Begin
  ShowProgressBar();
  MaxSteps := 1000;
  For i:=1 to MaxSteps do begin
     Self.Progress := I;
     UpdateProgressBar();
     DoSomething();
  end;
  HideProgressBar();
End;
Die fraglichen Methoden sind also 'ShowProgressBar', 'UpdateProgressBar' und 'HideProgressBar'.
In jedem Fall müssen die erst einmal per 'Synchronize' (oder 'Queue') aufgerufen werden, denn diese beiden Methoden sorgen dafür, das die Aufrufe im Kontext des VCL-Threads durchgeführt werden.
Also:
Delphi-Quellcode:
Procedure TMyThread.Execute;
Begin
  Synchronize(ShowProgressBar);
  MaxSteps := 1000;
  For i:=1 to MaxSteps do begin
     Self.Progress := I;
     Synchronize(UpdateProgressBar);
     DoSomething();
  end;
  Synchronize(HideProgressBar);
End;
Ok. Nun gibt es (mindestens) zwei Ansätze, wie die konkrete Kommunikation mit einem Formular aussehen kann.
1. Mit Events. Der Thread deklariert drei Eventhandler ,'OnShowProgressBar', 'OnHideProgressBar' und 'OnUpdateProgressBar' (geht auch mit einem Event, aber egal). Das Formular meldet sich auf die Events an und führt die VCL-Änderungen (Progressbar zeigen, verbergen, updaten) aus. So ungefähr
Delphi-Quellcode:
Type
  TMyThread = Class...
  public
     OnShowProgressBar : TNotifyEvent Read FOnShowProgressBar Write FOnShowProgressBar;
     OnHideProgressBar : TNotifyEvent Read FOnHideProgressBar Write FOnHideProgressBar ;
     ...
   end;

Procedure TMyThread.ShowProgressbar;
Begin
  if Assigned (FOnShowProgressBar) Then
     FOnShowProgressBar(Self);
End;
...

Procedure TMyForm.StartThread;
Begin
  FmyThread := TmyThread.Create;
  FmyThread.OnShowProgressBar := ShowProgressBar;
  FmyThread.OnHideProgressBar := HideProgressBar;
...
End;

Procedure TMyForm.ShowProgressBar (Sender : TObject);
Begin
  Progressbar.Visible := True;
End;
Oder mit einem Interface. Dabei erwartet der Thread eine 'View', die die drei Methoden zum Anzeigen der Progressbar bereitstellt.
Delphi-Quellcode:
Type
  IProgessBar = interface
    procedure ShowProgressBar;
    Procedure HideProgressBar;
    Procedure UpdateProgressBar (Position, Total : Integer);
  End;

  TMyThread = class (TThread)
    FProgess : IProgressBar;
  public
    Constructor Create (aProgress : IProgress);
  End;

...
Procedure TMyThread.ShowProgressBar;
Begin
  if Assigned (FProgress) then FProgess.ShowProgressBar;
End;

Procedure TMyThread.UpdateProgressBar;
Begin
  if Assigned (FProgress) then FProgess.UpdateProgressBar (Self.Position, Self.Total);
End;
....

Type
  TMyForm = Class (TForm, IProgress)
  ...
    Procedure ShowProgressBar;
    Procedure HideProgressBar;
    Procedure UpdateProgressBar (Position, Total : Integer);
End;
Das geht alles viel einfacher und besser, aber das Prinzip sollte klar sein. Standardpattern bei Delphi sind die Events. In anderen Programmiersprachen eher die Interfaces (Java kennt z.B. gar keine Delegaten).
  Mit Zitat antworten Zitat
Blup

Registriert seit: 7. Aug 2008
Ort: Brandenburg
1.470 Beiträge
 
Delphi 12 Athens
 
#12

AW: Update-Vorgang in einen Thread auslagern

  Alt 19. Dez 2014, 10:30
Eigentlich ist das ein Fall für den asynchronen Prozeduraufruf.
Ältere Delphi-Versionen unterstützen das noch nicht.

In jedem Fall muss der Hauptthread die Ereignisse selbst auslösen.

A) Der Thread sendet Informationen über den Fortschritt mit Postmessage als Botschaft an ein Fenster, eine Komponente verarbeitet die Nachrichten und löst die Ereignisse aus.
Es ist aber nicht zu 100% sichergestellt, das wirklich alle Nachrichten auch den Empfänger erreichen, insbesondere wenn sehr viele Nachrichten in kurzer Zeit anfallen.

B) Der Thread legt Informationen über den aktuellen Progressstatus in einem geschützten Record ab, der Hauptthread prüft diesen Record periodisch (OnIDLE der Anwendung oder Timer).
Dabei können aber auch einzelne Schritte übersprungen werden, insbesondere wenn der Thread sehr schnell arbeitet.

C) Der Thread legt jede Ereignis z.B. als Objekt mit allen Informationen in einer geschützten Liste ab, der Hauptthread prüft die Liste periodisch (OnIDLE der Anwendung oder Timer).
Der Aufwand ist wahrscheinlich größer, das scheint aber die sicherste Variante zu sein.
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#13

AW: Update-Vorgang in einen Thread auslagern

  Alt 19. Dez 2014, 10:34
Na, asynchron muss das Update der Progressbar hier nun doch nicht sein. Besser wäre es natürlich, aber es geht auch so.

Bei zeitkritischen Threads, die ihre Zeit nicht mit dem Update einer UI verplempern wollen, ist das natürlich anzuraten (wobei dann ein Timer in der UI noch einfacher umzusetzen ist. EDIT: Ach, das ist ja deine Antwort 'B').

Geändert von Dejan Vu (19. Dez 2014 um 10:53 Uhr)
  Mit Zitat antworten Zitat
TakkaTukka

Registriert seit: 20. Dez 2014
1 Beiträge
 
#14

AW: Update-Vorgang in einen Thread auslagern

  Alt 20. Dez 2014, 11:44
Hallo !

Zitat:
1. Mit Events. Der Thread deklariert drei Eventhandler ,'OnShowProgressBar', 'OnHideProgressBar' und 'OnUpdateProgressBar' (geht auch mit einem Event, aber egal). Das Formular meldet sich auf die Events an und führt die VCL-Änderungen (Progressbar zeigen, verbergen, updaten) aus.
Hierzu habe ich eine Frage:
Wie muss das in der Form1 dann aussehen (komplette Deklaration), ich meine damit durch den Thread ein Event in Form1 oder Form2, .... ausgelöst wird, das ist mir vollkommen unklar ?
Ich denke damit kann man dann an mehreren Stellen im Programm auf Änderungen reagieren oder (ich denke hier z.B. an die Umstellung des Währungsformats, der Nachkommastellen, ....) ?
  Mit Zitat antworten Zitat
Benutzerbild von Mavarik
Mavarik

Registriert seit: 9. Feb 2006
Ort: Stolberg (Rhld)
4.143 Beiträge
 
Delphi 10.3 Rio
 
#15

AW: Update-Vorgang in einen Thread auslagern

  Alt 20. Dez 2014, 12:05
Das mit dem Interface sieht natürlich klasse im Source aus...

Aber warum sendest Du nicht einfach aus dem Thread eine User-Message über Windows und schon sparst Du Dir das Syncronize!

Delphi-Quellcode:
Const
         WM_PShow = WM_User + 400;
         WM_PHide = WM_USer + 401;
         WM_PUpdate = WM_USer + 402;

type
         TMyForm = class(TForm)
           private
             procedure WMShowPBar(var Msg:Tmessage); message WM_PShow;
             procedure WMHidePBar(var Msg:Tmessage); message WM_PHide;
             procedure WMUpdatePBar(var Msg:Tmessage); message WM_PUpdate;
         end;
Für Update einfach im WParam 0..100% übergeben und fertig.

Mavarik
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#16

AW: Update-Vorgang in einen Thread auslagern

  Alt 20. Dez 2014, 12:21
Hey Mavarik: Messages hab ich total vergessen. Sehr schöne Möglichkeit, speziell mit 'PostMessage' (asynchron)

Was mir daran als Pattern nicht so sehr gefällt ist die etwas problematische Übergabe komplexerer Informationen. Denn die müssen im Thread instantiiert und im Messagehandler (beim Postmessage) wieder freigegeben werden. Geht, ist sauber, aber imho nicht so schön (reine Geschmackssache)

Was mir daran allerdings gefällt, ist der äußerst niedrige Footprint zum Erzeugen von asynchronen Updatemöglichkeiten.

Wie muss das in der Form1 dann aussehen (komplette Deklaration)
Delphi-Quellcode:
Type
  TForm1 = class (TForm)
  ...
    Procedure UpdateProgressBar (Sender : TObject); // Das ist die Signatur eines TNotifyEvent
  ...

Procedure TForm1.Button1Click(Sender : TObject);
Begin
  FMyThread := TMyThread.Create;
  FMyThread.OnUpdateProgressBar := UpdateProgressbar; // Zuweisung des Eventhandlers an das Event
...
Zitat:
...(ich denke hier z.B. an die Umstellung des Währungsformats, der Nachkommastellen, ....
Dafür gibt es fertige Messages, die von Windows versandt werden. (siehe Mavariks Ansatz). Googel mal danach.
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#17

AW: Update-Vorgang in einen Thread auslagern

  Alt 20. Dez 2014, 12:32
Eventuell weil das zu stark mit der Plattform verbunden ist?

Weil man sich dafür einen MessageManager schreibt, den man dann für alles benutzen kann und vor allem verständliche und einfach zu handhabende Messages durch das System leiten kann.

Auf den Trichter ist auch Emba gekommen System.Messaging.

Den Nachrichtentypen definieren:
Delphi-Quellcode:
unit MyCustomMessages;

uses
  System.Messaging;

type
  TProgressMessage = class( TMessage )
  public
    constructor Create( Position, Max : Integer );
    property Position : Integer read FProgress;
    property Max : Integer read FMax;
  end;
Die Form damit verbinden
Delphi-Quellcode:
uses
  System.Messaging;

TFoo = class( TForm )
  Progressbar1 : TProgressBar;
private
  procedure HandleProgressMessage( const Sender : TObject; const M : TMessage );
public
  procedure AfterConstruction; override;
  procedure BeforeDestruction; override;
end;

implementation

uses
  MyCustomMessages;

procedure TFoo.AfterConstruction;
begin
  inherited;
  TMessageManager.DefaultManager.SubscribeToMessage( TProgressMessage, HandleProgressMessage );
end;

procedure TFoo.BeforeDestruction;
begin
  TMessageManager.DefaultManager.Unsubscribe( TProgressMessage, HandleProgressMessge );
  inherited;
end;

procedure TFoo.HandleProgressMessage( const Sender : TObject; const M : TMessage );
var
  LMsg : TProgressMessage absolute M;
begin
  ProgressBar1.Position := LMsg.Position;
  ProgressBar1.Max := LMsg.Max;
end;
Der Thread schmeisst die Nachricht
Delphi-Quellcode:
uses
  System.Messaging, MyCustomMessages;

procedure TFooThread.Execute;
var
  LPosition, LMax : Integer;
begin
  
  while WorkInProgress do
  begin
    // Fortschritts-Nachricht verschicken
    Queue(
      procedure
      begin
        TMessageManager.DefaultManager.SendMessage( Self, TProgressMessage.Create( LPosition, LMax ) );
      end );
    ...
  end;
end;
Und schon klappt das auf jeder Plattform, auch ohne irgendwelche Fenster (Handle) offen zu haben, egal wer das empfangen möchte, kann sich einfach an den MessageManager hängen und alles ist gut.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Benutzerbild von Mavarik
Mavarik

Registriert seit: 9. Feb 2006
Ort: Stolberg (Rhld)
4.143 Beiträge
 
Delphi 10.3 Rio
 
#18

AW: Update-Vorgang in einen Thread auslagern

  Alt 20. Dez 2014, 12:34
Was mir daran als Pattern nicht so sehr gefällt ist die etwas problematische Übergabe komplexerer Informationen. Denn die müssen im Thread instantiiert und im Messagehandler (beim Postmessage) wieder freigegeben werden. Geht, ist sauber, aber imho nicht so schön (reine Geschmackssache)
Ja, das kommt darauf an was Du komplex nennest... Hierbei ging es "nur" um eine Zahl 0..100% (Byte)
Selbst wenn es ein mehr ist.. im thread ein guter alter Getmem und im Form der Freemem.
Da man das kaum noch verwendet - ist sofort klar - Oh ein Getmem... Da muss sich jemand anderes darum kümmern das wieder frei zu geben...

Mavarik
  Mit Zitat antworten Zitat
Benutzerbild von Mavarik
Mavarik

Registriert seit: 9. Feb 2006
Ort: Stolberg (Rhld)
4.143 Beiträge
 
Delphi 10.3 Rio
 
#19

AW: Update-Vorgang in einen Thread auslagern

  Alt 20. Dez 2014, 12:42
Und schon klappt das auf jeder Plattform, auch ohne irgendwelche Fenster (Handle) offen zu haben, egal wer das empfangen möchte, kann sich einfach an den MessageManager hängen und alles ist gut.
Klar, auch das sieht schick im Sourecode aus... Und ja... Natürlich hat Du wieder einmal Recht...
Aber er sprach von einem eigenen Installer - Sieht mir nicht nach iOS/Android oder Mac aus...

Also ist es - erst mal - nicht so schlimm, wenn es nur für Windows ist. Abgesehen davon spart man sich das Queue...

Mavarik

PS.: Du schreibst jetzt 100x "Ich soll keinen kompletten Sourcecode im Forum posten"
Miniaturansicht angehängter Grafiken
bart-simpson-generator.jpg  
  Mit Zitat antworten Zitat
Dejan Vu
(Gast)

n/a Beiträge
 
#20

AW: Update-Vorgang in einen Thread auslagern

  Alt 20. Dez 2014, 13:16
Ja, das kommt darauf an was Du komplex nennest... Hierbei ging es "nur" um eine Zahl 0..100% (Byte)
Selbst wenn es ein mehr ist.. im thread ein guter alter Getmem und im Form der Freemem.
Da man das kaum noch verwendet - ist sofort klar - Oh ein Getmem... Da muss sich jemand anderes darum kümmern das wieder frei zu geben...
Richtig. Ich meinte auch 'Pattern', also das, was ich versuche, grundsätzlich in Programmen zu verwenden.
Aber ansonsten hast Du natürlich vollkommen recht. Dein Ansatz ist absolut gleichrangig und reine Geschmackssache (oder Starrsinn), das ich es nicht verwende.

Also: bisher 4 schöne Pattern, um Aktualisierungen aus einem Thread in einen anderen zu übermitteln:
1. Events
2. Interfaces
3. Messages
4. Observer-Pattern (@Sir Rufo, einverstanden?)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 4     12 34      


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 23:45 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