Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Anwendung sauber beenden, während Thread läuft (https://www.delphipraxis.net/166219-anwendung-sauber-beenden-waehrend-thread-laeuft.html)

DeddyH 3. Feb 2012 11:01

Anwendung sauber beenden, während Thread läuft
 
Ich bastle momentan für einen Bekannten an einer Dateisuch-Komponente. Diese benutzt intern einen Thread, der bei Funden synchronisiert Events abarbeiten lässt. Soweit alles feini, feini. Das Problem, das ich noch habe (ich zeige mal beispielhaften Vergleichscode):
Komponente
Delphi-Quellcode:
type
  TMyThread = class(TThread)
  private
    FOnDings: TDingsEvent;

...

procedure TMyThread.DoOnDings;
begin
  if Assigned(FOnDings) then
    FOnDings(Parameter);
end;

procedure TMyThread.Execute;
begin
  //arbeiten
  if Dings then
    Synchronize(DoOnDings);
end;
Formular
Delphi-Quellcode:
procedure TMyForm.DoOnDings(Parameter);
begin
  EditDings.Text := Parameter;
end;
Wird nun das Programm geschlossen, kann es natürlich passieren, dass das Edit vor meiner Komponente freigegeben wird, während die synchronisierte Methode noch arbeitet. Damit endet der Zugriff auf EditDings logischerweise in einer AV. Natürlich kann man im OnClose des Formulars den Thread terminieren, aber das muss der Anwender meiner Komponente tun, ich hätte das lieber selbst irgendwie geregelt. Leider habe ich keine zündende Idee, momentan habe ich einfach den Aufruf der zugewiesenen Methode innerhalb des Threads in einen try-except-Block gepackt, wobei der Thread im Exception-Fall dann terminiert. Hat jemand einen schöneren Vorschlag?

Sir Rufo 3. Feb 2012 11:29

AW: Anwendung sauber beenden, während Thread läuft
 
Wenn die Anwendung beendet wird, dann sollte eigentlich Delphi-Referenz durchsuchenTApplication.Terminated auf True sein

WladiD 3. Feb 2012 11:53

AW: Anwendung sauber beenden, während Thread läuft
 
Also wenn dein Thread nicht mit FreeOnTerminate := TRUE läuft, dann reicht ein einfaches DeinThread.Free im OnDestroy des Forms aus und alles wird korrekt beendet.

Sir Rufo 3. Feb 2012 11:55

AW: Anwendung sauber beenden, während Thread läuft
 
Zitat:

Zitat von WladiD (Beitrag 1149029)
Also wenn dein Thread nicht mit FreeOnTerminate := TRUE läuft, dann reicht ein einfaches DeinThread.Free im OnDestroy des Forms aus und alles wird korrekt beendet.

Genau das möchte er doch vermeiden!

WladiD 3. Feb 2012 12:04

AW: Anwendung sauber beenden, während Thread läuft
 
Zitat:

Zitat von Sir Rufo (Beitrag 1149031)
Zitat:

Zitat von WladiD (Beitrag 1149029)
Also wenn dein Thread nicht mit FreeOnTerminate := TRUE läuft, dann reicht ein einfaches DeinThread.Free im OnDestroy des Forms aus und alles wird korrekt beendet.

Genau das möchte er doch vermeiden!

Aha, OK, hab's zu oberflächig gelesen. Aber der Anwender der Komponente muss doch etwas tun: Create und Free muss schon irgendwo im Code sein (ist ja kein TComponent-Nachkömmling). Und wenn FreeOnTerminate := FALSE ist, dann kann die oben genannte Zugriffsverletzung niemals entstehen.

Also man kann sich grundsätzlich niemals darauf verlassen, dass Sychronize erfolgreich ausgeführt wird, da man den Kontext aus dem Thread aus nicht kennen kann (und soll).

DeddyH 3. Feb 2012 12:14

AW: Anwendung sauber beenden, während Thread läuft
 
Vielleicht habe ich mich missverständlich ausgedrückt: Komponente auf Formular, Thread in Komponente. Komponente bekommt Event zugewiesen und schleust das einfach zum Thread durch. Somit führt der Thread eine Methode des Forms aus, auch wenn dieses bereits mit seiner Freigabe begonnen hat. Das im Formular abzufangen ist ja nicht das Thema, nur habe ich darauf keinen Einfluss.

himitsu 3. Feb 2012 12:23

AW: Anwendung sauber beenden, während Thread läuft
 
VCL-Komponenten sollten einen "ich werde grade gelöscht"-Status besitzen (ComponentState). Diesen könnte dein Thread ja abfragen.

Zitat:

Zitat von Sir Rufo (Beitrag 1149021)
Wenn die Anwendung beendet wird, dann sollte eigentlich Delphi-Referenz durchsuchenTApplication.Terminated auf True sein

Wobei ich mir wünschen würde, in der Unis System gäbe es einen Terminated-Boolean und eventuell auch noch ein Event dazu, auf welches man reagieren könnte.

Ist schön, wenn man ständig die Unit Forms einbinden muß, nur wegen diesem Status. :cry:

WladiD 3. Feb 2012 12:30

AW: Anwendung sauber beenden, während Thread läuft
 
Na in dem Fall muss die zu synchronisierende Methode nach csDestroying abfragen. In deinem Fall:

Delphi-Quellcode:
procedure TMyForm.DoOnDings(Parameter);
begin
  if not (csDestroying in ComponentState) then
    EditDings.Text := Parameter;
end;

DeddyH 3. Feb 2012 12:32

AW: Anwendung sauber beenden, während Thread läuft
 
An Application.Terminated oder Ähnliches hatte ich auch schon gedacht. Aber es kann ja auch sein, dass die Komponente auf einem dynamisch erzeugten Formular liegt, damit bekäme ich dasselbe Problem, sobald das freigegeben wird, da ja dann die Anwendung trotzdem weiterläuft.

@Wladi: das ist aber wieder Code, der dann im Formular stehen muss.

Sir Rufo 3. Feb 2012 12:33

AW: Anwendung sauber beenden, während Thread läuft
 
Aber irgendwie habe ich folgendes noch nicht verstanden:

Willst du jetzt
  1. eine Komponente (Ableitung von TComponent) entwickeln mit einem darin werkelnden Thread
  2. einen Thread, der auf eine Komponente (TEdit) zugreift
Im Falle von a) wie soll denn der Benutzer den Thread überhaupt beenden können und vor allem sollen? Der Thread sollte dann doch so gekapselt sein, dass man eben nicht direkt an den Thread kommt.

@himitsu
ja, die Eigenschaft ComponentState ist mir gerade auch noch eingefallen, allerdings sollte dieses nicht der Thread abfragen, sondern die Komponente selber

DeddyH 3. Feb 2012 12:38

AW: Anwendung sauber beenden, während Thread läuft
 
Es ist Fall a). Die Komponente stellt Methoden bereit, um indirekt auf den Thread Einfluss zu nehmen (diese heißen z.B. StopSearch, ContinueSearch, Abort usw.). Die direkte Kommunikation mit dem Thread übernimmt aber die Komponente, da der Thread nach außen gar nicht bekannt ist.

Sir Rufo 3. Feb 2012 12:40

AW: Anwendung sauber beenden, während Thread läuft
 
Ok, wie wäre denn sowas:
Delphi-Quellcode:
type
  TMyThreadComponent = class( TComponent )
  private
    FMyThread : TMyThread;
    FEdit :    TEdit;
    procedure SetEdit( const Value : TEdit );
  protected
    procedure DoEvent( const Info : string );
  public
    constructor Create( AOwner : TComponent ); override;
    destructor Destroy; override;
  published
    property Edit : TEdit read FEdit write SetEdit;
  end;


procedure TMyThreadComponent.DoEvent( const Info : string );
begin
  if Assigned( Edit ) and ( Self.ComponentState = [] )
  then
    Edit.Text := Info;
end;

DeddyH 3. Feb 2012 12:43

AW: Anwendung sauber beenden, während Thread läuft
 
Nützt mir leider nichts, da ich innerhalb der Komponente weder wissen kann noch will, was am Frontend geschieht, sobald ich ein Event auslöse. Ansonsten hätte ich auch FreeNotification oder sowas in Betracht gezogen.

Sir Rufo 3. Feb 2012 12:47

AW: Anwendung sauber beenden, während Thread läuft
 
Zitat:

Zitat von DeddyH (Beitrag 1149049)
Nützt mir leider nichts, da ich innerhalb der Komponente weder wissen kann noch will, was am Frontend geschieht, sobald ich ein Event auslöse. Ansonsten hätte ich auch FreeNotification oder sowas in Betracht gezogen.

Du willst und kannst nicht wissen, was am FrontEnd passiert, also wenn das Formular sich im Nirwana auflöst, willst es aber trotzdem wissen?

DeddyH 3. Feb 2012 12:51

AW: Anwendung sauber beenden, während Thread läuft
 
Du hast mein Dilemma erkannt :mrgreen: Ich glaube, ich lasse das jetzt so, dann wird eben in einer README darauf hingewiesen, dass man beim Schließen ggf. noch den Thread anhalten muss. Die Exception tritt sonst evtl. immer noch auf, wird aber ja von mir abgefangen und kommt somit nicht mehr hoch.

Sir Rufo 3. Feb 2012 12:56

AW: Anwendung sauber beenden, während Thread läuft
 
Also ich habe da mal auf die Schnelle zusammengehauen mit dem ComponentState und das funktioniert einwandfrei
Delphi-Quellcode:
procedure TMyThreadComponent.DoEvent( const Info : string );
begin
  if Assigned( Edit ) and ( Self.ComponentState = [] )
  then
    Edit.Text := Info;
end;
Die Komponente hat als Owner das Formular und sobald das Formular in die ewigen Jagdgründe geschickt wird, dann ändert sich erstmal der ComponentState von allen Components, die das Formular als Owner haben. Somit würden also diese Events zwar vom Thread noch aufgerufen aber nicht mehr wirklich bedient.

Und
Delphi-Quellcode:
( Self.ComponentState = [] )
prüft ja auch nicht irgendwas abstruses von ausserhalb ab, sondern den Status deiner eigenen Komponente

EDIT
Hier mal die gesamte Komponente (ist nicht schön und auch nicht wirklich ThreadSafe abgesichert)
Delphi-Quellcode:
unit uMyThreadComponent;

interface

uses
  Classes, Vcl.StdCtrls;

type
  TMyDoEvent = procedure( const Info : string ) of object;

  TMyThread = class( TThread )
  private
    FOnDoEvent : TMyDoEvent;
    procedure SetOnDoEvent( const Value : TMyDoEvent );
  protected
    procedure Execute; override;
    procedure DoEvent;
  public
    property OnDoEvent : TMyDoEvent read FOnDoEvent write SetOnDoEvent;
  end;

  TMyThreadComponent = class( TComponent )
  private
    FMyThread : TMyThread;
    FEdit :    TEdit;
    procedure SetEdit( const Value : TEdit );
  protected
    procedure DoEvent( const Info : string );
  public
    constructor Create( AOwner : TComponent ); override;
    destructor Destroy; override;
  published
    property Edit : TEdit read FEdit write SetEdit;
  end;

implementation

uses
  System.SysUtils;

{ TMyThreadComponent }

constructor TMyThreadComponent.Create( AOwner : TComponent );
begin
  inherited;
  FMyThread          := TMyThread.Create( True );
  FMyThread.OnDoEvent := DoEvent;
  FMyThread.Start;
end;

destructor TMyThreadComponent.Destroy;
begin
  FMyThread.Terminate;
  FMyThread.WaitFor;
  FMyThread.Free;
  inherited;
end;

procedure TMyThreadComponent.DoEvent( const Info : string );
begin
  if Assigned( Edit ) and ( Self.ComponentState = [] )
  then
    Edit.Text := Info;
end;

procedure TMyThreadComponent.SetEdit( const Value : TEdit );
begin
  FEdit := Value;
end;

{ TMyThread }

procedure TMyThread.DoEvent;
begin
  if Assigned( OnDoEvent )
  then
    FOnDoEvent( FormatDateTime( 'hh:nn:ss.zzz', now ) );
end;

procedure TMyThread.Execute;
begin
  while not Terminated do
    begin
      Synchronize( DoEvent );
      Sleep( 20 );
    end;
end;

procedure TMyThread.SetOnDoEvent( const Value : TMyDoEvent );
begin
  FOnDoEvent := Value;
end;

end.

DeddyH 3. Feb 2012 13:04

AW: Anwendung sauber beenden, während Thread läuft
 
Du hast mich gerade auf eine Idee gebracht, ich probiere mal etwas aus (mit ComponentState hatte ich auch schon probiert, kann mir aber denken, wo evtl. mein Denkfehler lag).

[edit] Danke für den Schubs in die richtige Richtung. Statt den Thread direkt das Event ausführen zu lassen, benachrichtigt er nun die Komponente, welche zuerst ihren ComponentState prüfen kann und dann das Event auslöst oder eben nicht. So scheint das zu klappen :) [/edit]

Sir Rufo 3. Feb 2012 16:37

AW: Anwendung sauber beenden, während Thread läuft
 
Zitat:

Zitat von DeddyH (Beitrag 1149055)
[edit] Danke für den Schubs in die richtige Richtung. Statt den Thread direkt das Event ausführen zu lassen, benachrichtigt er nun die Komponente, welche zuerst ihren ComponentState prüfen kann und dann das Event auslöst oder eben nicht. So scheint das zu klappen :) [/edit]

ja, wie denn auch sonst :mrgreen:

DeddyH 3. Feb 2012 16:41

AW: Anwendung sauber beenden, während Thread läuft
 
Das wirklich Lustige daran ist ja, dass ich das bei einem Event bereits genau so gemacht hatte. Wieso bei den anderen nicht, das wird wohl für immer ein Geheimnis meines wirren Hirns bleiben :mrgreen: (immer diese Stimmen *huuuu*).

haentschman 3. Feb 2012 16:48

AW: Anwendung sauber beenden, während Thread läuft
 
...oder die Medikamentendosis erhöhen :lol: Es gibt aber noch die Möglichkeit das deine Kaffemaschine kaputt ist... :gruebel:

DeddyH 3. Feb 2012 16:51

AW: Anwendung sauber beenden, während Thread läuft
 
Das sagt mein Kaktus auch immer, aber der hat keine Ahnung, der ist nämlich gelernter Bäcker :roteyes: :freak:

EWeiss 3. Feb 2012 22:17

AW: Anwendung sauber beenden, während Thread läuft
 
OT:

Zitat:

das wird wohl für immer ein Geheimnis meines wirren Hirns bleiben :mrgreen:
Der war gut.. aber mir gehts nicht anders.

gruss

TBx 4. Feb 2012 12:11

AW: Anwendung sauber beenden, während Thread läuft
 
[OT]
Zitat:

Zitat von haentschman (Beitrag 1149086)
...oder die Medikamentendosis erhöhen :lol: Es gibt aber noch die Möglichkeit das deine Kaffemaschine kaputt ist... :gruebel:

evtl. wäre da auch noch der too-much-beer-error in Betracht zu ziehen ;-)
[/OT]


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:23 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz