Autor Beitrag
crowley
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 406

Win XP, Win Vista, Mandriva, Ubuntu
Delphi 4-8, Delphi 2006, Delphi 2007
BeitragVerfasst: Fr 23.06.06 10:33 
Hallöli

ich habe ein irritierendes Problem: Also... ich möchte in einem eigenen Thread ein Form anzeigen, dass mir mittels Fortschrittsbalken anzeigt, dass "etwas" passiert... Schlussendlich ist es eine Progressbar, die auf und ab läuft. Prinzipiell funktioniert das auch hervorragend. Jetzt habe ich aber Probleme im destructor. Wenn ich im Hauptprogramm schreibe

ausblenden Delphi-Quelltext
1:
2:
  MyThread.Terminate;
  MyThread := nil;


habe ich keinerlei Problem. Wenn ich allerdings - in welcher Form auch immer - den destructor aufrufe, dann hängt sich die Anwendung auf...

ausblenden Delphi-Quelltext
1:
2:
  MyThread.Terminate;
  FreeAndNil(MyThread); { macht keinen Unterschied zu MyThread.Free + MyThread := nil}


Hier zuerst mal der Quellcode meines Thread- Objektes:

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:
unit GnProgressThread;

interface

uses
  Classes, GnProgressForm;

type
  TProgressThread = class(TThread)
  private
    FProgForm            : TProgressForm;

    procedure ShowProgress;
  public
    constructor Create(CreateSuspended: Boolean);
    destructor Destroy; override;

    procedure Execute; override;
  end;

implementation

{ TProgressThread }

uses Forms, SysUtils;

constructor TProgressThread.Create(CreateSuspended: Boolean);
begin
  inherited;

  FreeOnTerminate := true;
end;

destructor TProgressThread.Destroy;
begin
  if not Terminated then begin
    Terminate;
    
    if Suspended then
      Resume;

    WaitFor;
  end;

  if Assigned(FProgForm) then begin
    FProgForm.Close;
    FreeAndNil(FProgForm);
  end;

  inherited Destroy;
end;

procedure TProgressThread.Execute;
begin
  Synchronize(ShowProgress);
end;

procedure TProgressThread.ShowProgress;
begin
  FProgForm := TProgressForm.Create(nil);

  try
    FProgForm.Show;

    while not Terminated do begin
      if FProgForm.ProgressBar.Position < FProgForm.ProgressBar.Max then
        FProgForm.ProgressBar.Position := FProgForm.ProgressBar.Position + 1
      else 
        FProgForm.ProgressBar.Position := FProgForm.ProgressBar.Min + 1;

      Sleep(20);
      Application.ProcessMessages;
    end;
  finally
    FProgForm.Close;
    FreeAndNil(FProgForm);
  end;
end;

end.


Wenn der destructor so aufgerufen wird, bleibt er an dem WaitFor hängen... der Verzicht auf FreeOnTerminate machte keinen Unterschied...

In meinem MainForm habe ich einen Button mit folgenden Code integriert:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
procedure TTestMainForm.Button1Click(Sender: TObject);
begin
  if not Assigned(FThread) then begin
    FThread        := TProgressThread.Create(false);
  end else begin
    FThread.Terminate;       { <--- funktioniert}
    // FThread.Free;         { <--- funktioniert nicht}   
    // FreeAndNil(FThread);  { <--- funktioniert nicht}
    FThread := nil;          { <--- funktioniert, daher brauch ich das FreeOnTerminate}
  end;
end;


Hat da jemand eine Idee, was da verkehrt ist ?

C.
digi_c
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 1905

W98, XP
D7 PE, Lazarus, WinAVR
BeitragVerfasst: Fr 23.06.06 15:49 
Ich habe noch nichts groß mit Threads gemacht aber die VCL und die Forms sind doch nicht threadsave also musst du vielleicht mit Critical Sections arbeiten?
Grendel
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 443

Gentoo Linux, MacOS X, Win 2000
D5 Ent, D7 Ent, Lazarus, Anjuta, MonoDevelop
BeitragVerfasst: Fr 23.06.06 16:02 
Du musst nach dem Aufruf von "Terminate" noch mit "WaitFor" auf die Beendigung des Thread warten. Terminate beendet den Thread nicht sofort, sondern setzt die Eigenschaft "Terminated" auf True und signalisiert dem Thread dadurch, daß er sich demnächst doch beenden möge. WaitFor kehrt dann zurück, wenn der Thread wirklich durchgelaufen ist.
Danach sollte Free() auch keinen Fehler mehr werfen.

Bis neulich ...

edit: Hab Blödsinn geschrieben. Wenn Du FreeOnTerminate auf True setzt darfts Du natürlich nicht selber versuchen den Thread freizugeben. Nach dem Aufruf von Terminate und WaitFor genügt es, der Variablen einfach nil zuzuweisen. Mit Free bzw. FreeAndNil versuchst Du ja sonst ein Objekt freizugeben, daß garnicht mehr existiert.
Nur wenn FreeOnTerminate False ist muss Du selber Free aufrufen.
crowley Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 406

Win XP, Win Vista, Mandriva, Ubuntu
Delphi 4-8, Delphi 2006, Delphi 2007
BeitragVerfasst: Mo 26.06.06 08:46 
@Grendel... das Problem besteht im WaitFor... an dieser Stelle bleibt das Programm hängen... und ich habe keine Ahnung warum... *himpf*... ich habe das ganze mit FreeOnTerminate und auch ohne getestet... es macht keinen Unterschied... *seufz*


@digi_c... Wenn ich den Zugriff auf die VCL im Synchronize mache, brauche ich keine CriticalSections... das ist dann darin schon ausreichend gekapselt...
Grendel
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 443

Gentoo Linux, MacOS X, Win 2000
D5 Ent, D7 Ent, Lazarus, Anjuta, MonoDevelop
BeitragVerfasst: Mo 26.06.06 09:20 
Du darfst das WaitFor nicht innerhalb des Threads aufrufen. Schmeiß den ganzen Kram aus dem destructor des Threads:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
  if not Terminated then begin  
    Terminate;  
      
    if Suspended then  
      Resume;  

 
    WaitFor;  
  end;


Um den Thread zu Beenden rufst Du dann folgendes auf:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
FThread.Terminate();
FThread.WaitFor();
FThread.Free();   // <- nur bei FreeOnTerminate = False
FThread := nil;


Bis neulich ...
crowley Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 406

Win XP, Win Vista, Mandriva, Ubuntu
Delphi 4-8, Delphi 2006, Delphi 2007
BeitragVerfasst: Mo 26.06.06 09:29 
werd ich mal versuchen, danke dir ;)

hatte mich bei der implementation an den destructor in der (originalen) TThread-Klasse gehalten... da steht das WaitFor auch im destructor *gg*