Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Threads "korrekt" beenden (https://www.delphipraxis.net/128488-threads-korrekt-beenden.html)

FAlter 30. Jan 2009 17:11


Threads "korrekt" beenden
 
Hi,

es geht um folgendes:

Angenommen, ich habe eine Klasse von TThread abgeleitet und verwende sie so einfach wie möglich, indem ich einfach den Konstruktor aufrufe und den Rest dem Thread überlasse. FreeOnTerminate wird im Konstruktor auf true gesetzt, sodass der Thread nach seiner Arbeit automatisch zerstört wird.

In meinem Programm steht dann ja nur noch ein TMyThread.Create(...) drin, und da ich es nicht brauche, speichere ich auch das neue Objekt nirgends.

Nun kommt aber irgendwer auf die Idee, das Programm zu beenden während der Thread noch läuft. Dann wird dieser ja sofort abgebrochen.

Schöner wäre es ja, wenn erstmal nur die Methode Terminate des Threads aufgerufen wird und das Programm sich erst dann komplett beendet, wenn die gerade laufenden Threads allesamt auch beendet sind.

Nur wie kann ich das erreichen, wo ich doch keinen Zugriff mehr auf die Objekte habe?


Gibt es einen besseren Ansatz als die laufenden Threads in eine TObjectList einzutragen? Dann müsste ich bei FreeOnTerminate nämlich immer auch noch das OnTerminate entsprechend implementieren, damit sich die Threads auch selbst wieder rauslöschen, ansonsten würde die Liste ja bereits freigegebene Objekte enthalten. Was anderes fällt mir nämlich nicht ein auf das Ende des Threads zu reagieren.

Mfg
FAlter

[edit] Mit WM_QUERYENDSESSION im Thread abfragen geht es nicht unbedingt - es soll für alle Nachfahren von TThread gelten, auch für fremde... [/edit][edit]Und der Thread könnte ja eh nicht schnell genug darauf reagieren, da die Reaktion auch abgebrochen werden würde. :gruebel: [/edit]

Der.Kaktus 30. Jan 2009 17:19

Re: Threads "korrekt" beenden
 
Hi, schau Dir mal folgende Komponente an..vielleicht hilft sie Dir BMThread

FAlter 30. Jan 2009 17:31

Re: Threads "korrekt" beenden
 
Hi,

ich will keine Komponente haben, sondern den "normalen" TThread aus der Unit Classes nutzen. Das geht doch recht einfach. Konstruktor schreiben der benötigte Daten entgegennimmt, Execute überschreiben, fertig...

Und bevor die Anwendung ganz stirbt, also z. B. im OnDestroy des Hauptformulars, soll was getan werden, was das Programm dazu veranlässt, für die ganzen unbekannten Threads idealerweise die Methode Terminate aufzurufen und dann soll gewartet werden bis sich die Threads auch wirklich beendet haben.
Wenn ich die Threads irgendwo gespeichert habe, wäre das ja kein Problem, aber was tue ich eben wenn ich die Threads nicht kenne?

Mfg
FAlter

alzaimar 30. Jan 2009 17:47

Re: Threads "korrekt" beenden
 
Was hält Dich denn davon ab, dir die Instanz des Threads zu merken? Dann kannst Du ihn kontrolliert beenden. Übrigens führt es zu unschönen Seiteneffekten, wenn man Threads nicht selbst terminiert. Im Terminierungscode des Threads werden Resourcen aus der Unit 'Classes' benötigt, die beim Beenden der Anwendung u.U. schon freigegeben, d.h. nicht mehr vorhanden sind. Das kann zu unschönen AV-Meldungen führen, deren Herkunft nur sehr schwer zu ermitteln ist.

Daher solltest Du immer alle Threads VOR Ausführung der Finalisierungssequenz (z.B. im OnClose/OnDestroy deines Hauptformulars) beenden.

FAlter 30. Jan 2009 17:49

Re: Threads "korrekt" beenden
 
Hi,

das heißt also es führt kein Weg daran vorbei die Thread-Objekte zu speichern? Und wenn ich das richtig verstehe ist FreeOnTerminate nicht sinnvoll?

Mfg
FAlter

Der.Kaktus 30. Jan 2009 20:17

Re: Threads "korrekt" beenden
 
Zitat:

Zitat von FAlter
Hi,

ich will keine Komponente haben, sondern den "normalen" TThread aus der Unit Classes nutzen. Das geht doch recht einfach. Konstruktor schreiben der benötigte Daten entgegennimmt, Execute überschreiben, fertig...

Und bevor die Anwendung ganz stirbt, also z. B. im OnDestroy des Hauptformulars, soll was getan werden, was das Programm dazu veranlässt, für die ganzen unbekannten Threads idealerweise die Methode Terminate aufzurufen und dann soll gewartet werden bis sich die Threads auch wirklich beendet haben.
Wenn ich die Threads irgendwo gespeichert habe, wäre das ja kein Problem, aber was tue ich eben wenn ich die Threads nicht kenne?

Mfg
FAlter

vielleicht was abgucken? ;-)

alzaimar 30. Jan 2009 21:26

Re: Threads "korrekt" beenden
 
Zitat:

Zitat von FAlter
das heißt also es führt kein Weg daran vorbei die Thread-Objekte zu speichern?

Wenn Du es 'richtig' machen willst, eigentlich nicht.
Zitat:

Zitat von FAlter
Und wenn ich das richtig verstehe ist FreeOnTerminate nicht sinnvoll?

Doch, aber nur dann, wenn Du einen Thread hast, der 'nur kurz' etwas macht, oder von dem Du genau weisst, wie (und wann!) er terminiert.

Sir Rufo 30. Jan 2009 23:57

Re: Threads "korrekt" beenden
 
Und wenn du sowas nimmst ... eine selbstverwaltende Thread-Klasse?
Delphi-Quellcode:
unit CtrlThread;

interface

uses
  Classes;

type
  TCtrlThread = class( TThread )
  public
    constructor Create(CreateSuspended: Boolean);
    destructor Destroy; override;
  end;

implementation

uses
  Contnrs,
  SyncObjs;

var
  CtrlThreadList : TObjectList;
  CS : TCriticalSection;

procedure KillAllThreads;
var
  CTIdx : Integer;
begin
  while
    ( CtrlThreadList.Count > 0 )
  do
    begin

      CS.Enter;
      try

        for
          CTIdx := 0
        to
          CtrlThreadList.Count - 1
        do
          begin
            TCtrlThread( CtrlThreadList.Items[ CTIdx ] ).FreeOnTerminate := True;
            TCtrlThread( CtrlThreadList.Items[ CTIdx ] ).Terminate;
          end;

      finally
        CS.Leave;
      end;

    end;
end;

{ TCtrlThread }

constructor TCtrlThread.Create(CreateSuspended: Boolean);
begin
  inherited Create( CreateSuspended );

  CS.Enter;
  try
    CtrlThreadList.Add( Self );
  finally
    CS.Leave;
  end;
end;

destructor TCtrlThread.Destroy;
var
  CTIdx : Integer;
begin

  CS.Enter;
  try
    CTIdx := CtrlThreadList.IndexOf( Self );
    if
      ( CTIdx >= 0 )
    then
      CtrlThreadList.Delete( CTIdx );
  finally
    CS.Leave;
  end;

  inherited;
end;

initialization

  CS := TCriticalSection.Create;
  CS.Enter;
  try
    CtrlThreadList := TObjectList.Create;
    CtrlThreadList.OwnsObjects := False;
  finally
    CS.Leave;
  end;

finalization

  KillAllThreads;

  CS.Enter;
  try
    CtrlThreadList.Free;
  finally
    CS.Leave;
    try
      CS.Free;
    except
    end;
  end;

end.

alzaimar 31. Jan 2009 07:27

Re: Threads "korrekt" beenden
 
Solange der Finalization-Abschnitt immer VOR dem der Unit 'Classes' abgearbeitet wird, ist die Welt in Ordnung. Da viele Threads aber auch von anderen Units/Subsystemen abhängen, ist mir persönlich das nicht sicher genug. Wenn Du den Finalization-Code jedoch in einer exportierten Prozedur kapselst, kann der Anwender selbst bestimmen, wann die Threadliste aufgeräumt wird. Dann würde das doch richtig gut passen. Und wenn der Anwender vergisst, aufzuräumen, dann macht das eben dein Code... So etwa:
Delphi-Quellcode:
Unit CtrlThread;
...
Prozedure TerminateThreadList;
implementation
...
Procedure TerminateThreadList;
Begin
  If Not Assigned (CtrlThreadList) Then Exit;
  KillAllThreads;
  CS.Enter;
  try
    FreeAndNil(CtrlThreadList);
  finally
    CS.Leave;
    try
      CS.Free;
    except
    end;
  end;
end;

finalization
  TerminateThreadList;
end.

Sir Rufo 31. Jan 2009 09:23

Re: Threads "korrekt" beenden
 
Der Einwand ist berechtigt und darum habe ich die KillAllThreads jetzt als class procedure definiert. So kann man aufräumen wann man will, muss aber nicht ...
Delphi-Quellcode:
unit CtrlThread;

interface

uses
  Classes;

type
  TCtrlThread = class( TThread )
  public
    class procedure KillAllThreads;
    constructor Create(CreateSuspended: Boolean);
    destructor Destroy; override;
  end;



implementation

uses
  Contnrs,
  SyncObjs;

var
  CtrlThreadList : TObjectList;
  CS : TCriticalSection;

{ TCtrlThread }

class procedure TCtrlThread.KillAllCtrlThreads;
var
  CTIdx : Integer;
begin
  while
    ( CtrlThreadList.Count > 0 )
  do
    begin

      CS.Enter;
      try

        for
          CTIdx := 0
        to
          CtrlThreadList.Count - 1
        do
          begin
            TCtrlThread( CtrlThreadList.Items[ CTIdx ] ).FreeOnTerminate := True;
            TCtrlThread( CtrlThreadList.Items[ CTIdx ] ).Terminate;
          end;

      finally
        CS.Leave;
      end;

    end;
end;

constructor TCtrlThread.Create(CreateSuspended: Boolean);
begin
  inherited Create( CreateSuspended );

  CS.Enter;
  try
    CtrlThreadList.Add( Self );
  finally
    CS.Leave;
  end;
end;

destructor TCtrlThread.Destroy;
var
  CTIdx : Integer;
begin

  CS.Enter;
  try
    CTIdx := CtrlThreadList.IndexOf( Self );
    if
      ( CTIdx >= 0 )
    then
      CtrlThreadList.Delete( CTIdx );
  finally
    CS.Leave;
  end;

  inherited;
end;

initialization

  CS := TCriticalSection.Create;
  CS.Enter;
  try
    CtrlThreadList := TObjectList.Create;
    CtrlThreadList.OwnsObjects := False;
  finally
    CS.Leave;
  end;

finalization

  TCtrlThread.KillAllThreads;

  CS.Enter;
  try
    CtrlThreadList.Free;
  finally
    CS.Leave;
    try
      CS.Free;
    except
    end;
  end;

end.


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:32 Uhr.
Seite 1 von 2  1 2      

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