AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt
Thema durchsuchen
Ansicht
Themen-Optionen

Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt

Ein Thema von Rob09 · begonnen am 5. Okt 2011 · letzter Beitrag vom 8. Okt 2011
Antwort Antwort
Seite 2 von 2     12   
Benutzerbild von sx2008
sx2008

Registriert seit: 15. Feb 2008
Ort: Baden-Württemberg
2.332 Beiträge
 
Delphi 2007 Professional
 
#11

AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt

  Alt 6. Okt 2011, 00:05
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
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#12

AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt

  Alt 6. Okt 2011, 01:09
Ich würde das so umsetzen:

Hier der Erzeuger (die MainForm):
Delphi-Quellcode:
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.
und hier der Thread
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.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Rob09

Registriert seit: 14. Aug 2007
58 Beiträge
 
Delphi 6 Personal
 
#13

AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt

  Alt 6. Okt 2011, 19:06
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:
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;
ThreadManager:
Delphi-Quellcode:
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);
Thread:
Delphi-Quellcode:
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;
Man führt also sämtliche Befehle an den Thread über den Umweg des ThreadManagers durch.

Um dann nochmal auf omatas Idee zurückzugreifen... dem kommt das folgende recht nahe:

Thread:
Delphi-Quellcode:
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;
MainForm:
Delphi-Quellcode:
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);
Dabei muss man allerdings aufpassen, dass man die Terminierung des Thread immer per
Code:
.TerminateAndNil
anfordert.

Der Knackpunkt war der, dass dafür gesorgt werden muss, dass die Refernz auf den Thread jeweils auf
Code:
nil
zeigt, sofern der Thread gerade nicht läuft (bzw. bereits den Terminierungsbefehl erhalten hat).

Das ganze Schlamassel ist dadurch entstanden, dass ein Thread beim Terminieren teilweise ewig lang braucht, wenn er noch
Code:
CoUnitialize
aufruft. Darauf soll der Benutzer aber nicht warten.

Die entscheidende Frage, die sich nun daraus ergibt und beide Lösungen betrifft, ist letztlich die: Wird beim Schließen des Hauptfensters das
Code:
CoUninitialize
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?

Wäre schön, wenn jemand darauf eine Antwort hätte. (Es sei mir bitte verziehen, das das jetzt etwas Off-Topic geworden ist )

Beste Grüße!
Robert
  Mit Zitat antworten Zitat
Benutzerbild von Sir Rufo
Sir Rufo

Registriert seit: 5. Jan 2005
Ort: Stadthagen
9.454 Beiträge
 
Delphi 10 Seattle Enterprise
 
#14

AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt

  Alt 6. Okt 2011, 19:17
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.
Kaum macht man's richtig - schon funktioniert's
Zertifikat: Sir Rufo (Fingerprint: ‎ea 0a 4c 14 0d b6 3a a4 c1 c5 b9 dc 90 9d f0 e9 de 13 da 60)
  Mit Zitat antworten Zitat
Namenloser

Registriert seit: 7. Jun 2006
Ort: Karlsruhe
3.724 Beiträge
 
FreePascal / Lazarus
 
#15

AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt

  Alt 6. Okt 2011, 19:32
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 nil 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.

Aber ganz cool finde ich es trotzdem, deshalb hänge ich es mal an.
Angehängte Dateien
Dateityp: zip AutomaticReferences.zip (47,7 KB, 2x aufgerufen)
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.337 Beiträge
 
Delphi 11 Alexandria
 
#16

AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt

  Alt 6. Okt 2011, 19:41
@NamenLozer

Meintest Du diesen?
Ich hatte mir einen Link bisher verkniffen, da es durch die Thread-Verwendung wohl nicht ganz passt...
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Rob09

Registriert seit: 14. Aug 2007
58 Beiträge
 
Delphi 6 Personal
 
#17

AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt

  Alt 8. Okt 2011, 14:20
Okay, danke euch allen; glaube, ich bin jetzt ganz zufrieden

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:
procedure TMyThread.Execute;
begin
  ...
  CSAbbruch.Acquire;
  // ANFANG EINSCHUB ...
  if Terminated then
  begin
    CoUninitialize; //*
    CSAbbruch.Release; //*
    Exit;
  end;
  // ... ENDE EINSCHUB
  ArbeiteKritisch(...);
  ...
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" ) auf... (hier der Link: http://www.delphipraxis.net/163649-p...es-sauber.html)

Beste Grüße!
Robert

Geändert von Rob09 ( 8. Okt 2011 um 14:57 Uhr)
  Mit Zitat antworten Zitat
Rob09

Registriert seit: 14. Aug 2007
58 Beiträge
 
Delphi 6 Personal
 
#18

AW: Prüfen, ob Referenz auf tatsächlich existentes Objekt zeigt

  Alt 8. Okt 2011, 19:44
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.
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 2     12   


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:10 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz