Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2785
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:

Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)

Beitrag von m.fuchs »

kralle hat geschrieben: Sa 8. Mär 2025, 23:15 - FreeAndNil: Abfrage auf NIL fehlt. Warum? Ursachenforschung.
Aus Neugier: was ist denn damit gemeint?
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Benutzeravatar
kralle
Lazarusforum e. V.
Beiträge: 1187
Registriert: Mi 17. Mär 2010, 14:50
OS, Lazarus, FPC: Manjaro Linux, Mint und Windows 10 ,Lazarus 3.99, FPC-Version: 3.3.1
CPU-Target: 64Bit
Wohnort: Bremerhaven
Kontaktdaten:

Re: Doppeltes FreeAndNil

Beitrag von kralle »

Moin Michael,

"Alter Pascaler" sagte an einen paar Beispielen, welche Probleme es gibt, wenn man speicher nicht wieder freigibt.
Wir sprachen dann auch darüber ob man oder

Code: Alles auswählen

freeandnil
einsetzen sollte.

So kam es das man Testhalber zweimal

Code: Alles auswählen

freeandnil
im Code untereinander stehen hatte.
Das zweite

Code: Alles auswählen

freeandnil
führte dann zu einer Fehlermeldung, weil es im Code keine Abfrage auf "nil" gibt.

Die anderen können das vielleicht noch besser erklären, was wir dann aber vielleicht in einen eignen Thread auslagern sollten.

Gruß Kralle
OS: Manjaro Linux, Linux Mint und Windows 10
FPC-Version: 3.3.1 , Lazarus 3.99
+ Delphi XE7SP1

Benutzeravatar
m.fuchs
Lazarusforum e. V.
Beiträge: 2785
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: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)

Beitrag von m.fuchs »

Das verstehe ich nicht, du meinst folgende Problemstellung?

Code: Alles auswählen

program FreeAndNilTest;
{$MODE ObjFPC}
{$H+}

uses
  Classes, SysUtils;

var
  o: TObject;

begin
  o := TObject.Create;
  FreeAndNil(o);
  FreeAndNil(o);
  WriteLn('Done.');
end.
Das führt - wie eigentlich auch zu erwarten - zu keinem Problem. Wenn .Free bei einem Objekt aufgerufen wird welches nil ist, passiert eigentlich gar nichts. Deswegen wäre eine zusätzliche Abfrage innerhalb von FreeAndNil überflüssig.
Software, Bibliotheken, Vorträge und mehr: https://www.ypa-software.de

Benutzeravatar
AlterPascaler
Beiträge: 80
Registriert: Mo 26. Jun 2023, 18:56
OS, Lazarus, FPC: Linux, Lazarus, Free Pascal
CPU-Target: xxBit
Wohnort: Deutschland, NRW

Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)

Beitrag von AlterPascaler »

Es ging darum aufzuzeigen wo die Unterschiede zwischen

Code: Alles auswählen

begin
  o := TObject.Create;
  FreeAndNil(o);
  FreeAndNil(o);
  WriteLn('Done.');
end.
und

Code: Alles auswählen

begin
  o := TObject.Create;
  o.Free;
  o.Free;
  WriteLn('Done.');
end.
sind.

Weiter haben wir uns damit beschäftigt, wo die Unterschiede zwischen "o.Free" und "o,Destroy" sind, und ob man FreeAndNil verwenden soll, da es Fehler evtl. verschleiert haben wir auch diskutiert, kamen aber auf keinen gemeinsamen Nenner.

Viele Grüße
AP
Viele Grüße
AlterPascaler

Benutzeravatar
kralle
Lazarusforum e. V.
Beiträge: 1187
Registriert: Mi 17. Mär 2010, 14:50
OS, Lazarus, FPC: Manjaro Linux, Mint und Windows 10 ,Lazarus 3.99, FPC-Version: 3.3.1
CPU-Target: 64Bit
Wohnort: Bremerhaven
Kontaktdaten:

Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)

Beitrag von kralle »

Moin,

"Nummer8" und "Pluto" haben doch auch in den Quellcode von

Code: Alles auswählen

FreeAndnil
geschaut und dann wurde doch gesagt, das in der Procedure/Funktion eine Abfrage auf "nil" fehlen würde oder habe ich die Diskussion in dem Punkt falsch verstanden?

Gruß Kralle
OS: Manjaro Linux, Linux Mint und Windows 10
FPC-Version: 3.3.1 , Lazarus 3.99
+ Delphi XE7SP1

wp_xyz
Beiträge: 5095
Registriert: Fr 8. Apr 2011, 09:01

Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)

Beitrag von wp_xyz »

Aber die nil-Abfrage ist doch in Free enthalten:

Code: Alles auswählen

      procedure TObject.Free;

        begin
           // the call via self avoids a warning
           if self<>nil then
             self.destroy;
        end; 

Benutzeravatar
kralle
Lazarusforum e. V.
Beiträge: 1187
Registriert: Mi 17. Mär 2010, 14:50
OS, Lazarus, FPC: Manjaro Linux, Mint und Windows 10 ,Lazarus 3.99, FPC-Version: 3.3.1
CPU-Target: 64Bit
Wohnort: Bremerhaven
Kontaktdaten:

Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)

Beitrag von kralle »

Jetzt bin ich verwirrt.
In dem Code, den ich sah, war die Abfrage nicht vorhanden.

Gruß Kralle
OS: Manjaro Linux, Linux Mint und Windows 10
FPC-Version: 3.3.1 , Lazarus 3.99
+ Delphi XE7SP1

siro
Beiträge: 750
Registriert: Di 23. Aug 2016, 14:25
OS, Lazarus, FPC: Windows 11
CPU-Target: 64Bit
Wohnort: Berlin

Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)

Beitrag von siro »

Guten Morgen,
ohne jetzt zusätzlich Verwirrung stiften zu wollen, vielleicht fehlt mir auch noch ein Kaffee... :wink:
wundere ich mich jetzt auch:

Ich bin mal mit der rechten Maustaste auf die Zeile FreeAndNil(o) gegangen
dann "Finde Deklaration FreeAnd Nil" geklickt.
es öffnet sich die Datei sysutilh.inc

Dort steht:

Code: Alles auswählen

procedure FreeAndNil(var obj); 
nun klicke ich auch hier auf FreeAndNil und dann rechte Maustaste auf
"Suchen > springe zur Prozedur FreeAndNil"

nun lande ich in sysutils.inc und dort steht:

Code: Alles auswählen

procedure FreeAndNil(var obj);
      var
        temp: tobject;
      begin
        temp:=tobject(obj);
        pointer(obj):=nil;
        temp.free;
      end;        
Also habe ich dort mal einen Breakpoint gesetzt, aber da landet er garnicht..... :roll:
muss ich evtl. Lazarus neu compilieren mit Debuginfo...

wo landet er denn beim Aufruf von FreeAndNil(o); bzw. wo ist diese Procedure definiert.

--edit:
dem Assemblerfenster nach könnte das aber ein solcher Code sein ??
debug_FreeAndNil.png
debug_FreeAndNil.png (19.93 KiB) 4292 mal betrachtet
[Klugscheissermodus ein]übrigens wäre das NilAndFree und nicht FreeAndNil, was ich für Multithreads sogar SEHR sinnvoll finde :P
Grüße von Siro
Bevor ich "C" ertragen muß, nehm ich lieber Lazarus...

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1609
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)

Beitrag von fliegermichl »

Ich verwende FreeAndNil auch häufig. Oft habe ich Felder in einer Klasse, welche Zeiger auf Strukturen enthalten, welche an unterschiedlichen Stellen freigegeben werden müssen.

Code: Alles auswählen

procedure TMyClass.method1;
begin
 if fListe <> nil then FreeAndNil(fListe);
 ...
end;
Würde ich stattdessen nur Free aufrufen, würde die Referenz in fListe nicht auf nil gesetzt und ein weiterer Aufruf von method1 würde eine Zugriffsverletzung hervorrufen.
Welchen Fehler das verschleiern soll, ist wiederum mir schleierhaft.

wp_xyz
Beiträge: 5095
Registriert: Fr 8. Apr 2011, 09:01

Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)

Beitrag von wp_xyz »

fliegermichl hat geschrieben: Mo 10. Mär 2025, 08:21 Welchen Fehler das verschleiern soll, ist wiederum mir schleierhaft.
Zum Beispiel sowas:

Code: Alles auswählen

type
  TMyObject = class
    DoSomething;
  end;
var
  m: TMyObject;
begin  
  m := TMyObject.Create;
  // --- viel Code dazwischen
  FreeAndNil(m);
  // --- viel Code dazwischen
  if m <> ni then m.DoSomething;
Das ist im wirklichen Leben natürlich über verschiedene Prozeduren und evtl sogar Units verteilt, und teilweise schwer zu finden. Hier soll mit der Instanz m etwas gemacht werden, aber m ist schon zerstört, wenn darauf zugegriffen wird. Dadurch, dass der Programmierer routinemäßig FreeAndNil anwendet und beim Zugriff konsequent auf nil prüft, läuft das Programm sauber durch. Dennoch arbeitet es falsch, weil das m.DoSomething nicht ausgeführt wird; schlimmstenfalls merkt das erst der Kunde. Wäre statt FreeAndNil(m) nur m.Free verwendet worden, wäre das Programm schon beim Testlauf beim DoSomething abgestürzt, und der Programmierer hätte mitgekriegt, dass ein Fehler vorliegt.

Ich will FreeAndNil nicht verteufeln und setze es selbst immer wieder ein, aber man muss das mit Bedacht tun, z.B. bei Klassen, die einen weiten Gültigkeitsbereich haben (was an sich aber auch schon wieder ein Problem sein kann)
Zuletzt geändert von wp_xyz am Mo 10. Mär 2025, 11:30, insgesamt 1-mal geändert.

wp_xyz
Beiträge: 5095
Registriert: Fr 8. Apr 2011, 09:01

Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)

Beitrag von wp_xyz »

siro hat geschrieben: Mo 10. Mär 2025, 06:35 Also habe ich dort mal einen Breakpoint gesetzt, aber da landet er garnicht..... :roll:
muss ich evtl. Lazarus neu compilieren mit Debuginfo...
TObject.Free und FreeAndNil sind RTL-Routinen. Um dort mit dem Debugger reinzukommen, musst du FPC (nicht nur Lazarus) mit Debug-Informationen neu kompileren.

Benutzeravatar
theo
Beiträge: 10809
Registriert: Mo 11. Sep 2006, 19:01

Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)

Beitrag von theo »

siro hat geschrieben: Mo 10. Mär 2025, 06:35 Also habe ich dort mal einen Breakpoint gesetzt, aber da landet er garnicht..... :roll:
muss ich evtl. Lazarus neu compilieren mit Debuginfo...
Was erwartest du davon?
Du kannst den Code ja auch so verfolgen.
FreeAndNil ruft halt TObject.Free auf und dies wiederum macht nach einem Nil check TObject.Destroy.
Ende Gelände.

Warf
Beiträge: 2105
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)

Beitrag von Warf »

wp_xyz hat geschrieben: Mo 10. Mär 2025, 10:16 Ich will FreeAndNil nicht verteufeln und setze es selbst immer wieder ein, aber man muss das mit Bedacht tun, z.B. bei Klassen, die einen weiten Gültigkeitsbereich haben (was an sich aber auch schon wieder ein Problem sein kann)
Um da mal ein bisschen weiter drauf einzugehen, also das Grundproblem ist das das Manuelle Speicherverwalten ein sehr schweres Problem ist, im sinne von wissen wann Speicher im code nicht mehr benötigt wird kann je nach Code sehr kompliziert werden. Deshalb gibt es mehrere Methoden damit umzugehen, Grundsätzlich bewährt sich ein Ownership model.
Die Grundannahme dabei ist das ein Objekt in einem von zwei Stati sein kann. Entweder ein Objekt ist Valide und man darf drauf Zugreifen, oder es ist das nicht und man darf es nicht. Die Idee hinter dem Ownership Modell ist damit recht simpel, es gibt einen Owner der die Lebenszeit des Objektes vorgibt, und jeder andere Teil des Codes der dieses Objekt benutzt muss die Lebenszeit des Owners beherzigen.

Als Beispiel nehmen wir mal einen sehr einfachen Fall, eine Lokale Instanz einer TStringList um eine .ini Datei zu lesen:

Code: Alles auswählen

procedure LoadConfig(const FileName: String);
var
  sl: TStringList;
begin
  sl := TStringList.Create;
  try
    sl.LoadFromFile(FileName);
    LoadConfigPart1(sl);
    LoadConfigPart2(sl);
  finally
    sl.Free;
  end;
end;
In diesem Fall ist der owner von SL die Funktion LoadConfig, enforced durch ein try-finally was dafür sorgt das sl auf jeden Fall freigegeben wird. Die Funktionen LoadConfigPart1 und LoadConfigPart2 benutzen dabei SL, d.h. es muss sichergestellt werden das ihre Laufzeit nicht länger geht als die von SL. Das wird damit erreicht das SL als Parameter übergeben wird, und da die Funktionen fertig werden müssen damit zurück in LoadConfig gesprungen wird, ist sichergestellt das die Lebenszeit nicht überschritten werden kann. Wenn SL stattdessen als Globale Variable übergeben würde, wäre diese garantie nicht gegeben, da dann LoadConfigPart1 aus einer anderen Funktion aufgerufen werden könnte, nachdem LoadConfig fertig ist und SL bereits gefreed wurde.

Im prozeduralen ist das ganze sehr einfach Umzusetzen, in OOP wird das ganze schon komplizierter, da nun Objekte andere Objekte halten können. Dabei ist der Owner normalerweise ein Objekt mit längerer Laufzeit. Beispiel im Component System von Lazarus, kan jede Komponente andere Komponenten enthalten, und im Destruktor der Komponente werden dann alle Enthaltenen Komponenten gefreed.

Das Problem ist nun wenn man Nil mit reinnimmt gibts plötzlich einen Dritten Status: Valide aber nicht vorhanden. Beispiel von oben:

Code: Alles auswählen

procedure LoadConfig(const FileName: String);
var
  sl: TStringList;
begin
  // SL nicht zugewiesen, zugriff darauf nicht erlaubt
  sl := TStringList.Create;
  try
    // SL zugewiesen zugriff erlaubt
    sl.LoadFromFile(FileName);
    LoadConfigPart1(sl);
    LoadConfigPart2(sl);
  finally
    sl.Free;
  end;
  // SL nicht mehr zugewiesen zugriff nicht erlaubt
end;
Der FPC bietet verschiedene Hilfsmittel an um das zu enforcen. Zum einen wenn man versucht auf SL zuzugreifen bevor es zugewiesen wurde gibts eine Warning. Wenn man mit DataFlowAnalysis (-Oodfa) kompiliert, ist das Tracking von Lebenszeiten sogar deutlich akkurater. Wenn man mit -gt kompiliert werden variablen mit Müll initialisiert, sodass zugriff auf sl auch zur Laufzeit einen schönen Crash verursacht (der in den Tests einfach zu finden ist). Sowohl Valgrind als auch Heaptrc können Use-after-free tracken, Valgrind direkt durch Augmentierung, HeapTrc hat den KeepReleased Switch sodass objekte nach dem Free mit Müll befüllt werden, sodass Zugriffe darauf dann crashen.

Es gibt also wirklich einen Haufen an tricks die man machen kann um sicherzustellen das die Lebenszeiten Respektiert werden und man nicht Objekte anfasst die nicht mehr existieren.

Mit Nil wirds jetzt aber Kompliziert:

Code: Alles auswählen

procedure LoadConfig(const FileName: String);
var
  sl: TStringList = nil;
begin
  // SL ist zugewiesen und damit valide, verweist aber auf kein Objekt
  sl := TStringList.Create;
  try
    // SL zugewiesen und verweist auf Objekt
    sl.LoadFromFile(FileName);
    LoadConfigPart1(sl);
    LoadConfigPart2(sl);
  finally
    FreeAndNil(sl);
  end;
  // SL ist zugewiesen aber verweist auf kein Objekt
end;
Jetzt ist zu jedem Zeitpunkt im Programm SL valide, was Grundsätzlich vielleicht wie ein Vorteil klingt, aber es ist Valide aber nicht benutzbar. Das heißt die ganzen tollen tools die ich oben angesprochen hab, DFA, Trash Initialisierung, Valgrind/HeapTrc, funktionieren nicht mehr. Weil das Programm jetzt besser Definiert ist, ist es schwerer Fehlerzustände zu finden und damit können Fehler die man vorher einfach gefunden hätte jetzt untergehen.

Auch Informationstheoretisch ist es eine ganz andere Hausnummer, die Anzahl an States in denen ein Programm sein kann ist rein Kombinatorisch die Möglichen Stati des Objektes hoch der Anzahl der der Möglichen Zugriffe auf das Objekt, also wenn man Binär denkt, ein Objekt ist entweder Valide oder Invalide, bei 5 Zugriffen gibts Theoretisch nur 2^5=32 mögliche Kombinationen. Wenn man noch Nil als dritten Zustand hinzufügt ists 3^5=243. Hierbei explodiert die Komplexität sehr schnell.

Es gibt situationen in denen ist das Ownership Modell nicht das richtige, und Nil sehr nützlich. Beispiel hierfür ist wenn man einen Thread hat und wenn der Thread fertig ist, freed er sich selbst und setzt die Thread variable auf Nil (natürlich synchronized/locked). Der Main thread kann dann schauen ob die Thread variable Nil ist oder nicht, ob der Thread noch läuft. Das funktioniert weil hier das Ownership prinzip nicht greift, da der Thread vom Mainthread erstellt wird, von sich selbst aber gefreed wird, somit gibt es keinen klaren Owner der die Lebenszeit vorgibt, und NIL wird als Signal verwendet ob das Objekt noch am leben ist oder nicht.
Das Ding ist, während es solche Fälle gibt, sind sie extrem selten. Die allermeisten vorkommen von Klassennutzung sind innerhalb des Ownership Modells. Entweder als Teil einer Funktion (wie SL oben), oder als Feld einer Klasse (und wird im Destruktor gefreed).
In all diesen Fällen braucht man kein NIL, und wie oben beschrieben, ist die Einführung von NIL sogar kontraproduktiv, da hierbei viele der Mechanismen die FPC bereitstellt um solche Fehler zu finden, aktiv umgangen werden.

Daher ist meine Faustregel: Nil sollte man nur benutzen wenn man es aktiv braucht, wenn man nicht vor hat nil aktiv zu verwenden, dann sollte man einfach die Finger von lassen

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1609
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)

Beitrag von fliegermichl »

Naja, ich benutze das sehr häufig.
In meinem CAD Programm haben viele Objekte zusätzliche Eigenschaften, welche aber nur bei Bedarf ermittelt werden.
Beim ersten Zugriff auf die Eigenschaft prüfe ich, ob diese noch nil ist.

In dem Fall wird eine Methode aufgerufen, welche die zusätzlichen Informationen erstellt (z.B. Area einer Fläche). Das Ergebnis wird dann einem privaten Feld zugewiesen.
Bei einem erneuten Zugriff sind die entsprechenden Informationen schon vorhanden und müssen nicht neu ermittelt werden (Cache System)

Zwischendrin kann dann aber auch schonmal die Methode Invalidate aufgerufen werden. (z.B. ein zusätzlicher Punkt wurde in eine Fläche eingefügt)
Dann wird das lokale Feld wieder mit FreeAndNil freigegeben.

Im Destructor wird geprüft, ob das Feld <> nil ist und dann eben freigegeben.

Benutzeravatar
AlterPascaler
Beiträge: 80
Registriert: Mo 26. Jun 2023, 18:56
OS, Lazarus, FPC: Linux, Lazarus, Free Pascal
CPU-Target: xxBit
Wohnort: Deutschland, NRW

Re: Doppeltes FreeAndNil (WAS: 3. Norddeutsches Lazarustreffen)

Beitrag von AlterPascaler »

Wahrscheinlich nerve ich schon wieder den einen oder anderen, aber ich möchte an dieser Stelle nochmal auf TInterfaceObject hinweisen.
Bei Objekten wo ich ganz schwer die Lebensdauer abschätzen kann, verwende ich TInterfacedObject. Seitdem ist das Entwicklerleben viel leichter geworden. TInterfacedObject hat die schöne Eigenschaft das es sich selber auflöst, wenn es nicht mehr gebraucht wird.
Realisiert wird das durch einen internen Referenzzähler. Bei jeder Zuweisung wird der Zähler um 1 erhöht. Bei jeder Auflösung (Verlassen einer Funktion oder Klasse) wird der Referenzzähler um 1 erniedrigt. Beim wechsel von 1 auf 0 wird die Klasse freigegeben.

Hier ein kleines Demo Programm was das Verhalten von TInterfacedObject zeigen soll.
Also übt nicht soviel Kritik:)

Code: Alles auswählen

program demo_ifo_01;

{$mode objfpc}{$H+}

uses
  {$IFDEF UNIX}
  cthreads,
  {$ENDIF}
  Classes
  { you can add units after this };

type
  ICfg = interface
    function Data : String;
  end;

  { TICfg }

  TICfg = class(TInterfacedObject, ICfg)
  private
    CfgData: String;
  public
    constructor Create(const aCfgData: String);
    destructor destroy; override;
    function Data : String;
  end;

  { TCfgThread }

  TCfgThread = class(TThread)
  private
    Cfg: ICfg;
  protected
    procedure Execute; override;
  public
    constructor Create(aCfg: ICfg);
  end;

{ TICfg }

constructor TICfg.Create(const aCfgData: String);
begin
  inherited Create;
  CfgData:= aCfgData;
  WriteLn('TICfg.Create');
end;

destructor TICfg.Destroy;
begin
  WriteLn('TICfg.Destroy');
  inherited;
end;

function TICfg.Data: String;
begin
  Result:= CfgData;
end;

{ TCfgThread }

procedure TCfgThread.Execute;
begin
  while not Terminated do begin
    Sleep(Random(5000));
    WriteLn(Cfg.Data);
    Terminate;
  end;
end;

constructor TCfgThread.Create(aCfg: ICfg);
begin
  Cfg:= aCfg;
  FreeOnTerminate:= True;
  inherited Create(False);
end;

procedure DemoIfo;
var
  cfg: ICfg;
begin
  cfg:= TICfg.Create('InterfacedObject Demo');
  TCfgThread.Create(cfg);
  TCfgThread.Create(cfg);
  TCfgThread.Create(cfg);
  // Es gibt kein cfg.Free, und trotzdem wird aufgeräumt wenn das Object
  // nicht mehr gebraucht wird.
end;

begin
  Randomize;
  DemoIfo;
  TThread.Sleep(7000);
  WriteLn('Bye');
end.      
Viele Grüße
AP
Viele Grüße
AlterPascaler

Antworten