![]() |
Delphi-Version: 6
Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
Hi!
Okay, folgendes: Wenn ich prüfen möchte, ob ein Objekt existiert, dann kann ich ja einfach folgendes abfragen:
Delphi-Quellcode:
.
if Assigned(ReferenzAufObjekt) then ...
Das funktioniert aber nur dann, wenn ich beim zerstören des Objekts
Delphi-Quellcode:
verwende (oder halt
FreeAndNil(ReferenzAufObjektObjekt)
Delphi-Quellcode:
).
ReferenzAufobjekt := nil
Wenn ich allerdings das Objekt nur mit
Delphi-Quellcode:
zerstöre, dann liefert
ReferenzAufObjekt.Free
Delphi-Quellcode:
ja weiterhin True.
Assigned(...)
Gibt es denn irgendeine Möglichkeit, auch dann zu prüfen, ob das Objekt, auf das ReferenzAufObjekt zeigt, tatsächlich existiert oder schon zerstört wurde? Beste Grüße! Robert |
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
Ja, drauf zugreifen und wenn es kracht, existiert es nicht. aber wenn du um die Problematik weißt, warum gibst du es dann nicht ordentlich frei?
|
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
Zitat:
Somit bleibt dann wohl nur ein TInterfacedObject oder das Anwendungsdesign nochmal zu überdenken. |
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
Das tu ich deshalb, weil ich einen TThread benutze, der sich durch
Delphi-Quellcode:
selbst zerstört, wenn er fertig ist (vgl.
.FreeOnTerminate = True
![]()
Delphi-Quellcode:
gesetzt.
nil
An Anderer Stelle möchte ich prüfen, ob der Thread noch läuft - dafür würde ich das oben nachgefragte dann brauchen. Problem ist, dass ich mich etwas scheue, in der
Delphi-Quellcode:
-Prozedur des Threads die Referenz auf ihn
OnTerminate
Delphi-Quellcode:
zu setzen, da es durch
nil
Delphi-Quellcode:
etwas dauern kann, bis der Thread terminiert. Da der Aufruf zum
CoUninitialize
Delphi-Quellcode:
aus der
Terminate
Delphi-Quellcode:
-Prozedur des Hauptfensters kommen kann (das nicht
OnClose
Delphi-Quellcode:
abwartet, bevor es schließt), habe ich etwas Angst, was dabei passieren könnte.
Thread.WaitFor
|
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
Wie wäre es mit einem Event, dass beim Beenden des Threads eine Prozedur aufruft, die die globale Variable auf den Thread auf nil setzt?
|
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
@omata:
Du meinst also beim "regulären" Fertigwerden des Threads? Leider kann es auch sein, dass der Thread mit .Terminate abgebrochen wird, ohne dass das Programm geschlossen wird. In dem Fall müsste dann auch dafür gesorgt werden, dass die Referenz nil gesetzt wird. Wenn der .Terminate-Aufruf dann allerdings doch beim Schließen des Hauptfensters geschieht, stehe ich wieder vor dem oben beschriebenen Problem. Oder habei ch dich falsch verstanden? |
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
Ja, genauso meine ich das.
Überschreib doch Terminate und sorge so dafür, dass das Event eben doch vorher noch ausgeführt wird. |
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
Ah, sehr gute Idee! :thumb:
Werde mich morgen (also heute wenns hell wird :wink:) mal ransetzen und dann Bericht erstatten. Vielen Dank an alle bis hierher und gute Nacht! :hi: Robert |
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
Okay, dabei dann doch noch eine Frage:
Wenn ich aus dem MainThread heraus das .Terminate des NebenThreads aufrufe, führt dann der MainThread den Code, der dabei in eventuellen OnMyTerminate-Events steht, aus (was ich meine ist, ob dieser Code dann zuerst ausgeführt wird und erst danach die Befehle, die im MainThread nach NebenThread.Terminate noch kommen)? |
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
Die Befehle in der ThreadEnd-Prozedur werden nicht vom MainThread ausgeführt, sondern von NebenThread und damit ist ein Kritischer Abschnitt erforderlich oder ein Synchronize.
|
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
Es muss einen geben, der ist der "Chef" bzw. Erzeuger und Zerstörer des Objekts.
Im Zweifel ist dies das Formular, von dem aus die Threads gestartet wurden. Die Abfolge ist wie folgt: 1. Formular erzeugt das Objekt (z.B. Datanbank-Objekt, TCP-Socket,...) 2. Formular erzeugt das Threadobjekt (CreateSuspended=True!!) und merkt sich das Threadobjekt 3. Formular übergibt das Objekt von 1. dem Thread (über ein Property) 4. Formular startet den Thread mit Aufruf thread.Resume Falls nun das Formular zerstört wird muss Folgendes getan werden 5. Formular signalisiert dem Thread sich zu beenden (thread.Terminate) sollte der Thread schon zuende sein, kann 5. und 6. übersprungen werden Wichtig: der Thread muss immer wieder prüfen, ob Terminated=True ist und falls ja, sofort die Execute-Methode verlassen 6. Formular wartet auf das Ende des Threads 7. Formular entsorgt alles was es selbst erzeugt hat; also das Objekt und auch das Threadobjekt |
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
Ich würde das so umsetzen:
Hier der Erzeuger (die MainForm):
Delphi-Quellcode:
und hier der Thread
unit view.Server.Main;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TForm1 = class( TForm ) Button1 : TButton; procedure Button1Click( Sender : TObject ); procedure FormCloseQuery( Sender : TObject; var CanClose : Boolean ); private fThreadDemo : TThread; fFormCloseRequest : Boolean; procedure ThreadDestroy( Sender : TObject ); public end; var Form1 : TForm1; implementation uses thread.Demo; {$R *.dfm} procedure TForm1.Button1Click( Sender : TObject ); begin if Assigned( fThreadDemo ) then Exit; fThreadDemo := TThreadDemo.Create( True ); with TThreadDemo( fThreadDemo ) do begin FreeOnTerminate := True; OnDestroy := Self.ThreadDestroy; Start; end; end; procedure TForm1.FormCloseQuery( Sender : TObject; var CanClose : Boolean ); begin if Assigned( fThreadDemo ) then begin fThreadDemo.Terminate; if not fFormCloseRequest then begin fFormCloseRequest := True; with TPanel.Create( Self ) do begin Parent := Self; Left := 0; Top := 0; Width := Self.ClientWidth; Height := Self.ClientHeight; Caption := 'Wir müssen noch auf den Thread warten!'; end; end; CanClose := False; end; end; procedure TForm1.ThreadDestroy( Sender : TObject ); begin if fThreadDemo = Sender then begin fThreadDemo := nil; if fFormCloseRequest then Close; end; end; end.
Delphi-Quellcode:
unit thread.Demo;
interface uses Classes; type TThreadDemo = class( TThread ) private fOnDestroy : TNotifyEvent; protected procedure Execute; override; procedure DoOnDestroy; public destructor Destroy; override; property OnDestroy : TNotifyEvent read fOnDestroy write fOnDestroy; end; implementation { TThreadDemo } destructor TThreadDemo.Destroy; begin Synchronize( DoOnDestroy ); inherited; end; procedure TThreadDemo.DoOnDestroy; begin if Assigned( OnDestroy ) then OnDestroy( Self ); end; procedure TThreadDemo.Execute; begin while not Terminated do Sleep( 5000 ); end; end. |
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
Schonmal vielen Dank für die Beteiligung!
Zunächst mal zu Sir Rufos Vorschlag: Gefällt mir eigentlich sehr gut, nur ist der Haken, dass ich auf keinen Fall dem Benutzer zumuten möchte, unnötig auf den Thread zu warten, da es keinen Schaden anrichtet, wenn er bei "unkritischen" Arbeiten unterbrochen wird. Es gibt allerdings auch "kritische" Arbeiten, die er in jedem Fall entweder ganz oder garnicht durchführen soll. Dazu habe ich folgende Lösung erarbeitet: MainForm:
Delphi-Quellcode:
ThreadManager:
type
TMainForm = class(TForm) procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private FThreadManager: TThreadmanager; end; implementation procedure TMainForm.FormCreate(Sender: TObject); begin FThreadManager := TThreadmanager.Create; end; procedure TMainForm.FormDestroy(Sender: TObject); begin FreeAndNil(FThreadManager); end;
Delphi-Quellcode:
Thread:
type
TThreadManager = class(TObject) private FMyThread: TMyThread; public destructor Destroy; override; procedure StarteThread; procedure BeendeThread; end; var CSAbbruch: TCriticalSection; implementation destructor TThreadManager.Destroy; begin BeendeThread; inherited Destroy; end; procedure TThreadManager.StarteThread; begin if Assigned(FMyThread) then Exit; // Falls der Thread gerade läuft, soll er nicht nochmal gestartet werden FMyThread := TMyThread.Create(True); // CreateSuspended = True FMyThread.FreeOnTerminate := True; {An dieser Stelle noch Werte übergeben} FMyThread.Resume; end; procedure TThreadManager.BeendeThread; begin if not Assigned(FMyThread) then Exit; CSAbbruch.Acquire; // Damit kein Abbruch an einer kritischen Stelle erfolgt FMyThread.Terminate; FMyThread := nil; CSAbbruch.Release; end; initialization CSAbbruch := TCriticalSection.Create; finalization FreeAndNil(CSAbbruch);
Delphi-Quellcode:
Man führt also sämtliche Befehle an den Thread über den Umweg des ThreadManagers durch.
type
TMyThread = class(TThread) private procedure ArbeiteUnkritisch(...); procedure ArbeiteKritisch(...); protected procedure Execute; override; end; implementation procedure TMyThread.Execute; begin CoInitialize; // Deshalb habe ich überhaupt erst die Probleme ... if Terminated then begin CoUninitialize; Exit; end; ArbeiteUnkritisch(...); if Terminated then begin CoUninitialize; Exit; end; CSAbbruch.Acquire; ArbeiteKritisch(...); CSAbbruch.Release; CoUninitialize; // ... bzw. eher deshalb end; procedure TMyThread.ArbeiteUnkritisch; begin {Hier wird gearbeitet und es steht natürlich haufenweise drin:} if Terminated then Exit; end; procedure TMyThread.ArbeiteKritisch; begin {Hier geschehen Dinge, die entweder gar nicht angefangen oder vollständig ausgeführt werden sollen, deshalb steht hier auch nirgends "if Terminated then Exit" und insbesondere erfolgt der Aufruf dieser Prozedur zwischen CSAbbruch.Acquire & .Release} end; Um dann nochmal auf omatas Idee zurückzugreifen... dem kommt das folgende recht nahe: Thread:
Delphi-Quellcode:
MainForm:
type
TMyThread = class(TThread) private procedure ArbeiteUnkritisch(...); procedure ArbeiteKritisch(...); protected procedure Execute; override; public TerminateAndNil(var AReferenz: TMyThread); end; implementation procedure TMyThread.Execute; begin CoInitialize; // Deshalb habe ich überhaupt erst die Probleme ... if Terminated then begin CoUninitialize; Exit; end; ArbeiteUnkritisch(...); if Terminated then begin CoUninitialize; Exit; end; CSAbbruch.Acquire; ArbeiteKritisch(...); CSAbbruch.Release; CoUninitialize; // ... bzw. eher deshalb end; procedure TMyThread.ArbeiteUnkritisch; begin {Hier wird gearbeitet und es steht natürlich haufenweise drin:} if Terminated then Exit; end; procedure TMyThread.ArbeiteKritisch; begin {Hier geschehen Dinge, die entweder gar nicht angefangen oder vollständig ausgeführt werden sollen, deshalb steht hier auch nirgends "if Terminated then Exit" und insbesondere erfolgt der Aufruf dieser Prozedur zwischen CSAbbruch.Acquire & .Release} end; procedure TMyThread.TerminateAndNil(var AReferenz: TMyThread); begin AReferenz := nil; Terminate; end;
Delphi-Quellcode:
Dabei muss man allerdings aufpassen, dass man die Terminierung des Thread immer per
type
TMainForm = class(TForm) procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private FMyThread: TMyThread; public procedure StarteThread; procedure BeendeThread; end; var CSAbbruch: TCriticalSection; implementation procedure TMainForm.StarteThread; begin if Assigned(FMyThread) then Exit; FMyThread := TMyThread.Create(True); // CreateSuspended = True FMyThread.FreeOnTerminate := True; {Hier Werte übergeben} FMyThread.Resume; end; procedure TMainForm.BeendeThread; begin if not Assigned(FMyThread) then Exit; CSAbbruch.Acquire; // Damit kein Abbruch an einer kritischen Stelle erfolgt FMyThread.TerminateAndNil; CSAbbruch.Release; end; procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction); begin BeendeThread; end; initialization CSAbbruch := TCriticalSection.Create; finalization FreeAndNil(CSAbbruch);
Code:
anfordert.
.TerminateAndNil
Der Knackpunkt war der, dass dafür gesorgt werden muss, dass die Refernz auf den Thread jeweils auf
Code:
zeigt, sofern der Thread gerade nicht läuft (bzw. bereits den Terminierungsbefehl erhalten hat).
nil
Das ganze Schlamassel ist dadurch entstanden, dass ein Thread beim Terminieren teilweise ewig lang braucht, wenn er noch
Code:
aufruft. Darauf soll der Benutzer aber nicht warten.
CoUnitialize
Die entscheidende Frage, die sich nun daraus ergibt und beide Lösungen betrifft, ist letztlich die: Wird beim Schließen des Hauptfensters das
Code:
vom Thread noch durchgeführt? Wartet also die Anwendung mit dem Schließen so lange, bis auch der Thread terminiert hat, wobei nur der Benutzer nichts mehr davon sieht, oder killt die Anwendung beim Schließen des Hauptfensters rigoros den Thread, ohne auf dessen Terminierung zu warten?
CoUninitialize
Wäre schön, wenn jemand darauf eine Antwort hätte. (Es sei mir bitte verziehen, das das jetzt etwas Off-Topic geworden ist :duck:) Beste Grüße! Robert |
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
Leite den ThreadManager mal von TComponent ab und beim Erzeugen setzt du den Owner auf Application.
Dann kannst du dir das FreeAndNil in der MainForm auch sparen. Die CS global in der Unit wenn du nur einen Thread starten willst ok, ansonsten verschieb die CS in den Thread. |
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
Liste der Anhänge anzeigen (Anzahl: 1)
Ich hab vor einiger Zeit mal was gebastelt, wo mit allerlei Tricks Objekte bei ihrer Freigabe automatisch alle Referenzen (wofür leider ein bestimmter Typ verwendet werden muss, mit normalen Objektreferenzen oder Pointern klappt es nicht), die auf sie zeigen auf
Delphi-Quellcode:
setzen. Das ganze ist experimentell, für den produktiven Einsatz würde ich es nicht empfehlen. War auch eher eine Machbarkeitsstudie, die im Rahmen eines ähnliches Threads wie diesem vor einiger Zeit entstanden ist.
nil
Aber ganz cool finde ich es trotzdem, deshalb hänge ich es mal an. |
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
@NamenLozer
Meintest Du ![]() Ich hatte mir einen Link bisher verkniffen, da es durch die Thread-Verwendung wohl nicht ganz passt... |
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
Okay, danke euch allen; glaube, ich bin jetzt ganz zufrieden :wink:
Zur Ergänzung ist mir noch aufgefallen, dass in den Code-Vorschlägen in meinem letzten Post noch Nachbesserungsbedarf besteht. Und zwar muss folgendes noch eingeschoben werden:
Delphi-Quellcode:
Wenn man beide //*-Zeilen in dieser Reihenfolge stehen lässt, kann es natürlich sein, dass sich das Programm beim Beenden ein bisschen Zeit lässt, da CoUninitialize ein bisschen braucht. Wenn man die Reihenfolge umkehrt, geht alles fix. Allerdings weiß ich nicht, ob das dann noch wirklich "sauber" ist. Dazu mache ich gleich noch einen Thread (ein "Thema" :wink:) auf... (hier der Link:
procedure TMyThread.Execute;
begin ... CSAbbruch.Acquire; // ANFANG EINSCHUB ... if Terminated then begin CoUninitialize; //* CSAbbruch.Release; //* Exit; end; // ... ENDE EINSCHUB ArbeiteKritisch(...); ... ![]() Beste Grüße! Robert |
AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
:wall: Habe bei dem Code-Vorschlag, der den TThreadManager nutzt, übrigens auch nicht daran gedacht, dass der Thread ja auch regulär terminieren könnte, wobei dann auch FMyThread := nil gesetzt werden müsste. :coder2:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:48 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 by Thomas Breitkreuz