Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - Problem mit Delphi Thread


avenger - Mo 27.07.09 14:51
Titel: Problem mit Delphi Thread
Hallo zusammen,

Ich habe eine Frage bezueglich Threads in Delphi. Und zwar starte aus der FormCreate Methode einen Thread mit vTest := tTest.Create(false); Die Execute Methode wird also direkt ausgefuehrt. In der Execute Methode wird eine Componente NoDave erstellt und in einer while Schleife damit gearbeitet. (Es sollen waehrend der gesamten Programmausfuehrungen Daten aus einer SPS gelesen werden.) Die while Schleife soll erst dann verlassen werden, wenn das Programm beendet wird.
Also habe ich die Variable der while Schleife in der FormCloseQuery Methode auf false gesetzt. Die while Schleife sollte also eigentlich verlassen werden und der Code danach, der den Thread sowie die NoDave Komponente sauber beenden soll, ausgefuehrt werden. Nur leider bekomme ich lauter Fehlermeldungen wenn das Programm beendet wird, meistens dass „Speicher verloren geht“, ich gehe davon aus, dass der Thread und die NoDave Komponenten nicht sauber geschlossen worden. In der Tat, wenn ich das Programm debugge, wird nach Aufruf der FormCloseQuery die Execute Methode nicht mehr beendet??
Ich habe den passenden Quelltext unten angefuegt.

Ich habe auch schon versucht, die Execute Methode aus der FormCloseQuery nochmal mit einer booleschen Variable aufzurufen, die so gesetzt ist, dass sie dann nur die Schliessfunktionen fuer Thread und NoDave aufruft, aber nicht den Teil mit der Schleife aufruft. (Ich bin mir aber nicht sicher, ob dann „2 Instanzen“ vom Thread laufen?)

Wie auch immer, habt ihr eine Idee wie ich es sauber programmieren kann, dass ich einen Thread zum Programmstart erstelle, der eine NoDave Komponente erstellt und bis zum Programmstop eine Schleife durchlaeuft der die NoDave Komponenten bearbeitet und dann beim Programmstop den Thread und die NoDave Komponente, die nur im Thread bekannt ist, sauber beendet?



Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
unit Unit1;

interface

uses
  Windows, FastMM4, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, NoDaveComponent, AdvSmoothGauge, StdCtrls, AdvSmoothTrackBar,
  Menus, AdvMenus, ComCtrls, ExtCtrls, Buttons, AdvSmoothButton,
  AdvSmoothLedLabel, AdvSmoothSplashScreen, AdvSmoothLabel,
  AdvCircularProgress;

type
  TForm1 = class(TForm)
    NoDave1: TNoDave;
.....


   TTest = class(TThread)
   private

   protected
   //procedure update;

   public
   procedure execute; override;
   procedure update(MyNoDaveUpdate : TNoDave);
   end;



var
  Form1: TForm1;
  vTest : TTest;
  closeall:boolean;


implementation

{$R *.dfm}



procedure TTest.execute;
var
MyNoDave : TNoDave;
begin
if closeall = false then
begin

MyNoDave := TNoDave.Create(nil);


while closeall do
begin
update(MyNoDave);
end//while

MyNoDave.Disconnect;  //hier soll NoDave beendet werden
mynodave.Active:= false;
MyNoDave.Free;

vtest.FreeOnTerminate := True;  //hier soll der Thread beendet warden.
vtest.free;
end;



procedure TTest.update(MyNoDaveUpdate : TNoDave);
var
MyNoDave : TNoDave;
begin
//something
End:
End:


procedure TForm1.FormCreate(Sender: TObject);
begin
closeall:=true;
vTest := tTest.Create(false);   //start Thread
end;




procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
closeall:=false:      // hier versuche ich aus der while schleife in der execute methode herauszukommen
end;


Moderiert von user profile iconGausi: Delphi-Tags hinzugefügt


Gausi - Mo 27.07.09 15:04

Das Problem dürfte sein, dass du nicht auf die Beendigung des Threads wartest. Du rufst quasi in das Zimmer rein "Alle raus, ich schließ jetzt ab", knallst dann direkt die Tür zu, und wunderst dich dann, dass sich beim Abschließen von drinnen jemand besschwert. ;-)

Schau dir mal TThread.WaitFor an - damit sollte sich das beheben lassen.

Edit: Anstelle der CloseAll-Variable würde ich aber auch die Terminated-Property des Threads benutzen - für sowas ist das nämlich gedacht. ;-)


avenger - Mo 27.07.09 15:47

Hallo Gausi,


demnach sollte die FormCloseQuery und die execute so aussehen:? durch das waitfor wird also die execute methode vorm Schliessen noch komplett bis zum Ende durchlaufen?

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose:Boolean);
begin
vtest.Terminate;
vtest.WaitFor;

end;

und die execute dann so:
procedure TTest.execute;
varMyNoDave : TNoDave;
begin
if closeall = false then
beginMyNoDave := TNoDave.Create(nil);
while not Terminated  do
beginupdate(MyNoDave);
end//

whileMyNoDave.Disconnect;  //hier soll NoDave beendet werden
mynodave.Active:= false;
MyNoDave.Free;
vtest.FreeOnTerminate := True;  //hier soll der Thread beendet warden.
vtest.free;
end;

Vielen Dank

Moderiert von user profile iconNarses: Delphi-Tags hinzugefügt
Moderiert von user profile iconNarses: B- durch Highlight-Tags ersetzt


delfiphan - Mo 27.07.09 20:33

Am besten ohne FreeOnTerminate arbeiten und einfach das Thread-Objekt freigeben. Im Destruktor wird automatisch Terminate und WaitFor aufgerufen.

Wenn du FreeOnTerminate auf True setzst und gleichzeitig noch selbst freigibst, dann hast du's zweimal freigegeben. Wenn der Zeitpunkt der Freigabe klar definiert ist, brauchst du kein FreeOnTerminate.

Im Thread musst du aufpassen, dass die eingesetzten Komponenten Thread-Safe sind. Wie es mit NoDave aussieht, weiss ich nicht.