![]() |
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] |
Re: Threads "korrekt" beenden
Hi, schau Dir mal folgende Komponente an..vielleicht hilft sie Dir
![]() |
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 |
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. |
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 |
Re: Threads "korrekt" beenden
Zitat:
|
Re: Threads "korrekt" beenden
Zitat:
Zitat:
|
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. |
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. |
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 14:06 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-2025 by Thomas Breitkreuz