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).