doppelter Destruktoraufruf

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Antworten
Tobse82
Beiträge: 6
Registriert: Do 25. Okt 2012, 10:16

doppelter Destruktoraufruf

Beitrag von Tobse82 »

Hi Leute


ich bin ganz neu hier im Forum und auch ganz neu in Lazarus & Pascal. Ich habe jetzt 5 Jahre lang beruflich in C++ Server und Frontends entwickelt und habe nun den Auftrag mich mal bei Lazarus umzusehen ;)

Ich habe das Problem, dass mein destruktor doppelt aufgerufen wird. Ich weiß nicht wie das möglich ist.
Ich habe eine Klasse von TThread abgeleitet und von dieser Klasse wiederum meine aktuelle "Problem-Klasse" abgeleitet.
Ich stelle hier mehrfach für mich unlogisches Verhalten fest und die MemoryLeaks stapeln sich.

Hier erstmal meine Klasse:

Code: Alles auswählen

 
Type
  TSocketThread = class( TBaseThread )
  private
    ServerSocket     : TBlockSocket;
    IsSocketCreated  : Boolean;
 
  protected
    procedure Run; override;
    procedure SocketCreated( Sender: TObject ); virtual;
 
  public
    constructor Create; overload;                   // Konstruktor
    destructor Destroy; override;                   // Destruktor
 
    procedure Connect( ipadress, port : string );   // Erzeugt den Socket und stellt eine Verbindung zum Remote her
    procedure Disconnect;                           // Trennt die Verbindung und zerstoert den Socket
  end;
 
 
// =============================================================================
// Implementation der Klassenfunktionen
// =============================================================================
 
implementation
 
// Konstruktor
constructor TSocketThread.Create;
begin
  inherited Create( true );
  IsSocketCreated := false;
  ServerSocket := TBlockSocket.Create;
  ServerSocket.OnCreateSocket := @SocketCreated;
end;
 
 
destructor TSocketThread.Destroy;
begin
  //Disconnect;
  //ServerSocket.Destroy;
  inherited;
end; 

und ihre Verwendung:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
var
  Sock : TSocketThread;
begin
  Sock := TSocketThread.Create;
  Sock.Destroy;
end;   

Wie kann es sein, dass unter den Bedingungen der Destruktor ("Destroy") mehrfach aufgerufen wird.
Wenn ich das "Sock.Destroy" weglasse, wird er übrigens gar nicht aufgerufen- Daraus schließe ich, dass "Sock" auch nicht im Stack liegt und nicht automatisch von der procedure entsorgt wird.

Oder anders gefragt: Wie kann derart einfacher Quelltext schon soviel Ärger produzieren? :mrgreen:

gruß

Tobi

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2825
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: doppelter Destruktoraufruf

Beitrag von m.fuchs »

Schwer zu sagen, weil ne Menge Quellcode fehlt. Zum Beispiel von deinem TBaseThread . Wenn ich TSocketThread von TThread ableite, aus Run Execute mache und alles was mit ServerSocket zu tun hat auskommentiere, dann funktioniert es. Der Destructor wird nur einmal aufgerufen.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Tobse82
Beiträge: 6
Registriert: Do 25. Okt 2012, 10:16

Re: doppelter Destruktoraufruf

Beitrag von Tobse82 »

TBaseThread ist derzeit nur eine abgeleitete Klasse von TThread, die noch nichts tut

Code: Alles auswählen

Type
  TBaseThread = class( TThread )
  private
 
  protected
    procedure Execute; override;
    procedure Run; virtual; abstract;
 
  public
    constructor Create( CreateSuspended : boolean ); overload;
    destructor Destroy; override;
 
  end;
 
 
// =============================================================================
// Implementation der Klassenfunktionen
// =============================================================================
 
implementation
 
 
// Konstruktor
constructor TBaseThread.Create( CreateSuspended : boolean );
begin
  inherited Create( CreateSuspended );
  FreeOnTerminate := True;
end;
 
 
destructor TBaseThread.Destroy;
begin
   inherited Destroy;
end;
 

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2825
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: doppelter Destruktoraufruf

Beitrag von m.fuchs »

Tobse82 hat geschrieben:TBaseThread ist derzeit nur eine abgeleitete Klasse von TThread, die noch nichts tut

Code: Alles auswählen

FreeOnTerminate := True; 
Naja, das würde ich nun nicht als nichts bezeichnen. Setze FreeOnTerminate auf False und schon geht es.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Tobse82
Beiträge: 6
Registriert: Do 25. Okt 2012, 10:16

Re: doppelter Destruktoraufruf

Beitrag von Tobse82 »

Ok.Jetzt leuchtet mir auch die Funktion des Properties ein.

Ich bin es nur nicht gewohnt von C++, dass Destruktoren mehrfach aufgerufen werden, egal welches Property das auch immer befiehlt :D

Jetzt geht es jedenfalls erstmal. Danke dir ;) Ich merke, ich habe noch viel zu lernen.

MAC
Beiträge: 770
Registriert: Sa 21. Feb 2009, 13:46
OS, Lazarus, FPC: Windows 7 (L 1.3 Built 43666 FPC 2.6.2)
CPU-Target: 32Bit

Re: doppelter Destruktoraufruf

Beitrag von MAC »

Also ich muss sagen, der Code sieht für einen Einsteiger recht ordentlich aus.
zwei kleine Tipps:
1) Es wäre sauberer, statt Sock.Destroy Sock.Free zu verwenden, bzw FreeAndNil(Sock) zu verwenden. Letzteres ruft nicht nur den Destructor auf, sondern setzt Sock auf nil, sodas kein ungültiger Speicherzugriff geschehen kann.
2) Wenn eine Klasse von TThread abeleitet ist, dann sollte man diese nicht einfach so freigeben, sondern Sock.Terminate aufrufen. Und dann in der Execute procedure im Thread darauf reagieren (z.B: in einer While schleife immer abfragen ob Sock.Terminated = False ist und erst dann weiterarbeiten...). Wenn mann das so macht, kann man auch FreeOnTerminate := true setzen...

Code: Alles auswählen

Signatur := nil;

Tobse82
Beiträge: 6
Registriert: Do 25. Okt 2012, 10:16

Re: doppelter Destruktoraufruf

Beitrag von Tobse82 »

MAC hat geschrieben:Also ich muss sagen, der Code sieht für einen Einsteiger recht ordentlich aus.
zwei kleine Tipps:
1) Es wäre sauberer, statt Sock.Destroy Sock.Free zu verwenden, bzw FreeAndNil(Sock) zu verwenden. Letzteres ruft nicht nur den Destructor auf, sondern setzt Sock auf nil, sodas kein ungültiger Speicherzugriff geschehen kann.
2) Wenn eine Klasse von TThread abeleitet ist, dann sollte man diese nicht einfach so freigeben, sondern Sock.Terminate aufrufen. Und dann in der Execute procedure im Thread darauf reagieren (z.B: in einer While schleife immer abfragen ob Sock.Terminated = False ist und erst dann weiterarbeiten...). Wenn mann das so macht, kann man auch FreeOnTerminate := true setzen...

Erstmal danke für die Blumen :mrgreen: Ich geb mir immer, egal in welcher Sprache, Mühe, dass der Quellcode einigermaßen lesbar ist.
Die Inheritance-Konzepte von Pascal sind zudem denen von C++ ähnlich, insofern ist das rein von der Struktur her für mich nicht unbedingt neu ;)

Zu 2)
Du meinst, ich Rufe nicht TThread::Destroy auf, sondern TThread::Terminate, reagiere dann in der while-Schleife der Execute-Prozedur auf das Ereignis, verlasse die Schleife, lasse somit den Thread "auslaufen" und FreeOnTerminate sorgt dann dafür, dass das TThread::Objekt ordnungsgemäß, einschließlich aller Ableitungen, zerstört wird?

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2825
Registriert: Fr 22. Sep 2006, 19:32
OS, Lazarus, FPC: Winux (Lazarus 2.0.10, FPC 3.2.0)
CPU-Target: x86, x64, arm
Wohnort: Berlin
Kontaktdaten:

Re: doppelter Destruktoraufruf

Beitrag von m.fuchs »

Tobse82 hat geschrieben:Du meinst, ich Rufe nicht TThread::Destroy auf, sondern TThread::Terminate, reagiere dann in der while-Schleife der Execute-Prozedur auf das Ereignis, verlasse die Schleife, lasse somit den Thread "auslaufen" und FreeOnTerminate sorgt dann dafür, dass das TThread::Objekt ordnungsgemäß, einschließlich aller Ableitungen, zerstört wird?
Das wäre eben eine Möglichkeit. Im Prinzip eine Art Fire-and-Forget-Lösung. Das kommt ganz darauf an, wie deine restliche Anwendung strukturiert ist. Wenn bei dir ein Thread startet und dann getrennt vom Hauptthread laufen und dann verschwinden soll, ist das sicherlich die beste Lösung. Willst du einen Thread starten, der etwas tut und Daten in Feldern hält mit denen der Hauptthread später arbeiten soll, dann ist das händische Wegräumen des Threads der bessere Weg.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Antworten