Autor Beitrag
Geri
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 78

XP
RAD Studio XE pro
BeitragVerfasst: Sa 02.07.11 07:51 
Hallo zusammen

Ich habe eine Anwendung mit einer Thread. Trotz erfolgreichem Aufruf von Terminate erhalte ich mit memchk am Ende eine Meldung, dass die Thread 68 byte nicht freigegeben hat. Ich habe mal ein ganz kurzes Programm geschrieben, bei dem das Problem immer noch auftaucht. Es hat zwei Buttons. Einen um den Thread zu starten, einen um ihn vorzeigig zu beenden. Nach Beendigung der Thread wird zusätzlich ein Event onTerminate aufgerufen. Diese Methode wird auch wirklich aufgerufen. Ich habe mit Delphi 2007 und Delphi XE probiert.

ausblenden volle Höhe 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:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
unit uTestManCursor;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    btnCreate: TButton;
    btnTerminate: TButton;
    Memo1: TMemo;
    procedure btnCreateClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure btnTerminateClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
     FThread:TThread;
     procedure EnableButtons();
     procedure OnTerminate(Sender: TObject);
  end;

  TMyThread=class(TThread)
  private
    FTimeToWork:integer;
  protected
    procedure Execute;override;
  public
    constructor Create(TimeToWork:integer);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

constructor TMyThread.Create(TimeToWork: integer);
begin
  FTimeToWork:=TimeToWork;
  inherited Create(True);
end;


procedure TMyThread.Execute;
var
  T:Integer;
begin
  t:=FTimeToWork;
  Form1.Memo1.Lines.Add('Begin execution');
  while not Terminated and (t>0do
  begin
    Form1.Memo1.Lines.Add(format('Remaining %5.2f%%',[t/FTimeToWork*100]));
    Sleep(500);
    dec(t,500);
  end;
  if Terminated then
  Form1.Memo1.Lines.Add('Terminated by user');
  Form1.Memo1.Lines.Add('Finish execution');
end;


procedure TForm1.EnableButtons();
begin
  btnCreate.Enabled:=not Assigned(FThread);
  btnTerminate.Enabled:= Assigned(FThread);
end;


procedure TForm1.btnCreateClick(Sender: TObject);
  begin
  FThread:=TMyThread.Create(5000);
  FThread.OnTerminate:=OnTerminate;
  EnableButtons();
  FThread.Resume;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  EnableButtons;
end;


procedure TForm1.btnTerminateClick(Sender: TObject);
begin
  FThread.Terminate;
end;


procedure TForm1.OnTerminate(Sender: TObject);
begin
  FThread:=nil;
  EnableButtons();
end;

end.


Habt ihr vielleicht eine Idee, wo hier das Problem liegen könnte? Memchk als auch die bei Delphi eingebaute Speicherübewachung liefern die Meldung eines Lecks.

Danke für euere Hilfe und beste Grüsse

Geri
haentschman
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 285
Erhaltene Danke: 33


DX10 Berlin Professional
BeitragVerfasst: Sa 02.07.11 08:33 
Guten Morgen... :wave:
ausblenden Delphi-Quelltext
1:
2:
3:
if Terminated then
  Form1.Memo1.Lines.Add('Terminated by user');
  Form1.Memo1.Lines.Add('Finish execution');

- du greifst aus dem Thread heraus auf dein Form1 zu. Das geht nicht gut. Stichwort: Synchronize
Ein Prinzipbeispiel mit freunlichen Grüßen von der DP... wenn sie den Diebstahl bemerkt haben... :zwinker:
ausblenden volle Höhe 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:
type
  MeinForm = class(TMeinForm)
    lblThreadText: TLabel;
  private
    procedure StarteDenThread;
  end;

type
  Thread = class(TThread)
  private
    Form: TMeinForm;
    procedure MachVCLSachen;
  protected
    procedure Execute; override;
  public
    constructor Create(Form: TMeinForm);
  end;

procedure MeinForm.StarteDenThread;
begin
  Thread.Create(Self);
end;

constructor Thread.Create(Form: TMeinForm);
begin
  inherited Create(False);
  Self.Form := Form;
end;

procedure Thread.Execute;
begin
  // Kram machen
  Synchronize(MachVCLSachen);
  // Kram machen
end;

procedure Thread.MachVCLSachen;
begin
  Form.lblThreadText := 'Der Thread lässt grüßen!';
end;


PS: Ich denke, daß Deutsch nicht deine Muttersprache ist. Du kannst auch in Englisch schreiben wenn es für dich einfacher ist.
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Sa 02.07.11 08:59 
user profile iconhaentschman hat folgendes geschrieben Zum zitierten Posting springen:
PS: Ich denke, daß Deutsch nicht deine Muttersprache ist.
Sagst du das zu jedem Österreicher :lol: ? Ich würde mit solchen Vermutungen vorsichtig sein, nur weil in einem sonst nicht zu beanstandenen Text jemand den Genus von "Thread" anders gewählt hat, als du es tun würdest :gruebel: .

_________________
>λ=
haentschman
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 285
Erhaltene Danke: 33


DX10 Berlin Professional
BeitragVerfasst: Sa 02.07.11 09:16 
Ähmm... das war nicht böse gemeint. :oops: Tschuldigung, Sorry... ich hab nicht ins Profil geguckt.
Für mich las sich das beim Überfliegen wie ein automatisch übersetzter Text.
Zitat:
Ich habe mit Delphi 2007 und Delphi XE probiert.

Vieleicht war es auch nur zu früh für mich...
Teekeks
ontopic starontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic star
Beiträge: 211
Erhaltene Danke: 23



BeitragVerfasst: Sa 02.07.11 12:50 
user profile iconGeri hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
2:
3:
  public
    { Public declarations }
     FThread:TThread;

Das sollte doch eher so heißen:
ausblenden Delphi-Quelltext
1:
2:
3:
  public
    { Public declarations }
     FThread:TMyThread;

Oder?
SvenAbeln
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 334
Erhaltene Danke: 3



BeitragVerfasst: Sa 02.07.11 15:08 
Du gibst den Thread doch auch nirgendwo frei, da fehlt ein
ausblenden Delphi-Quelltext
1:
FThread.free;					
oder
ausblenden Delphi-Quelltext
1:
FThread.FreeOnTerminate := True;					
Boldar
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 1555
Erhaltene Danke: 70

Win7 Enterprise 64bit, Win XP SP2
Turbo Delphi
BeitragVerfasst: Sa 02.07.11 22:48 
user profile iconTeekeks hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconGeri hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
2:
3:
  public
    { Public declarations }
     FThread:TThread;

Das sollte doch eher so heißen:
ausblenden Delphi-Quelltext
1:
2:
3:
  public
    { Public declarations }
     FThread:TMyThread;

Oder?

Das ist in diesem Fall egal, Stichwort Polymorphismus. Tendenziell würde ich es sogar eher so wie hier vorziehen, da dann die Threadklasse leicht ersetztwerden kann, nämlich im Konstruktor der Form.
Was mich eher wundert, ist das Konstrukt im Create der Threadklasse, denn bei
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
constructor TMyThread.Create(TimeToWork: integer);
begin
  FTimeToWork:=TimeToWork;
  inherited Create(True);
end;

würde ja erst ein Feld gesetzt, bevor der Thread überhaupt erstellt wird...
Evtl. wird da schon der Standard-Konstruktor aufgerufen, und du rufst dann Create nochmals auf, wobei das alte Objekt im Nirwana landet? Du kannst ja vielleicht einfach den alten Konstruktor benutzen und dann FTimeToWork von aussen setzen (dann als property umbauen).
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19338
Erhaltene Danke: 1752

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 03.07.11 13:47 
user profile iconBoldar hat folgendes geschrieben Zum zitierten Posting springen:
Evtl. wird da schon der Standard-Konstruktor aufgerufen, und du rufst dann Create nochmals auf, wobei das alte Objekt im Nirwana landet? Du kannst ja vielleicht einfach den alten Konstruktor benutzen und dann FTimeToWork von aussen setzen (dann als property umbauen).
Nein, das ist genau der richtige Weg so...
Es wird zuerst das Feld gesetzt, damit der Wert in Execute schon zur Verfügung steht und dann wird der geerbte Konstruktor aufgerufen, der den Thread startet.
Auf die Weise kann der Thread auch sofort loslaufen und muss nicht pausiert erstellt werden. (Was er hier aber dennoch wird, deshalb geht es hier auch als Property.)

Wenn du den Wert erst hinterher setzt, musst du ja sicherstellen, dass Execute nicht vorher schon ausgeführt wird... (z.B. wie hier durch pausiertes Starten)
Geri Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 78

XP
RAD Studio XE pro
BeitragVerfasst: Mo 04.07.11 20:42 
Hallo zusammen

Vielen Dank für eure ausführliche Hilfe! Mit FreeOnTerminate hatte ich getestet. Wahrscheinlich hatte ich aber ein Brett vor dem Kopf...

Beste Grüsse und Danke nochmals!

Geri
PS: "die Thread" war nicht Absicht sondern ein Tippfehler.