AW: Arbeiten mit TThreadList
Zitat:
Vielleicht kam das erst mit XE? Aber ansonsten wäre es kein Unterschied, stattdessen mit (weitaus mehr Tippaufwand) sich auf klassischem Wege wieder eine Klasse
Delphi-Quellcode:
zu definieren,
TMyThread = class(TThread)
Delphi-Quellcode:
zu implementieren und den letztendlich zurückzugeben.
Execute()
|
AW: Arbeiten mit TThreadList
Ich verstehe gar nicht, was dieses Gehampel mit den Threads soll.
Delphi-Quellcode:
(ja, OwnsObjects auf True) und wenn diese Threads aus dem Speicher sollen, dann ein ganz lapidares freigeben der Liste.
TObjectList
Ein Thread ruft im Destroy ganz von alleine das TThread.Terminate auf, dann ein
Delphi-Quellcode:
.
TThread.WaitFor
Fazit: Die Thread-Klasse sauber aufbauen, dann klappt das wie von selber |
AW: Arbeiten mit TThreadList
Zitat:
|
AW: Arbeiten mit TThreadList
Apropos WaitFor. Darf ich mich mal kurz einklinken? Kann man das auch in den destructor schreiben oder ist es da schon zu spät?
Delphi-Quellcode:
constructor TFindSnapPointsThread.Create(List: TGraphicList);
begin inherited Create(true); FreeOnTerminate := true; FFindSnapPoints := TFindSnapPoints.Create; FList := List end; destructor TFindSnapPointsThread.Destroy; begin WaitFor; FFindSnapPoints.Free; inherited Destroy; end; procedure TFindSnapPointsThread.Terminate; begin FFindSnapPoints.Cancel := true; inherited Terminate; end; |
AW: Arbeiten mit TThreadList
Es sollte reichen, wenn du die Instanzen nach dem
Delphi-Quellcode:
freigibst.
inherited
Delphi-Quellcode:
Um hier aber etwas wirklich konkretes zu sagen, musst du dir dringend einmal anschauen, was in
destructor TFindSnapPointsThread.Destroy;
begin inherited; FFindSnapPoints.Free; end;
Delphi-Quellcode:
so passiert.
TThread.Destroy
In den neueren Delphi-Versionen wird dort eben genau das alles gemacht (
Delphi-Quellcode:
,
Terminate
Delphi-Quellcode:
,...).
WaitFor
Zitat:
Delphi-Quellcode:
zu klären sein, ob das wirklich nötig ist, oder ob es einfach reicht, den echten Destructor abzuwarten und dann erst alles einzureißen (s.o.)
TThread.Destroy
|
AW: Arbeiten mit TThreadList
Hier mal ein Beispiel-Thread, der sich nach außen hin "harmlos" verhält.
Delphi-Quellcode:
TMyForm = class( TForm )
procedure Button1Click( Sender:TObject ); private FMyThread : TMyThread; public procedure AfterConstruction; override; procedure BeforeDestruction; override; end; procedure TMyThread.Button1Click( Sender:TObject ); var LFoo : TFoo; begin // Feed the Thread ... LFoo := TFoo.Create; try FMyThread.WorkOnItem( LFoo ); LFoo := nil; // Wenn die Instanz vom Thread übernommen wurde, dann hier auf nil setzen finally LFoo.Free; // stellt bei einer Exception sicher, dass die Instanz freigegeben wird end; end; procedure TMyForm.AfterConstruction; begin inherited; FMyThread :=TMyThread.Create; end; procedure TMyForm.BeforeDestruction; begin FreeAndNil( FMyThread ); // erst wenn die Thread-Instanz wirklich freigegeben werden konnte, dann geht es hier weiter inherited; end;
Delphi-Quellcode:
UPDATE TThread.TerminatedSet ist mit XE2 gekommen
unit Unit1;
interface // In älteren Delphi-Versionen hat die Thread-Klasse noch keine TerminatedSet-Methode. // Dann bitte das nachfolgende {$DEFINE USE_TERMINATEDSET} ausschalten {$DEFINE USE_TERMINATEDSET } uses Classes, SyncObjs, Contnrs; type TMyThread = class( TThread ) private FCS : TCriticalSection; FEvent : TEvent; FToDoList : TObjectList; procedure DoWorkOnItem( Item : TObject ); function GetItem : TObject; protected procedure Execute; override; {$IFDEF USE_TERMINATEDSET} procedure TerminatedSet; override; {$ENDIF} public constructor Create; destructor Destroy; override; // Übergabe eines WorkItems an den Thread. // Der Thread übernimmt die Kontrolle über die Item-Instanz // und gibt diese bei Bedarf auch wieder frei // - Nach dem Abarbeiten // - Beim Beenden, wenn noch Items in der Liste enthalten sind procedure WorkOnItem( Item : TObject ); end; implementation { TMyThread } constructor TMyThread.Create; begin inherited Create( False ); // <-- NEIN, der Thread soll nie, nicht, niemals schlafen FCS := TCriticalSection.Create; FEvent := TEvent.Create( nil, False, False, '' ); FToDoList := TObjectList.Create( True ); end; destructor TMyThread.Destroy; begin {$IFDEF USE_TERMINATEDSET} // hier einfach nichts machen ... abwarten und Tee trinken {$ELSE} Terminate; FEvent.SetEvent; {$ENDIF} inherited; // jetzt alle Instanzen freigeben FToDoList.Free; FEvent.Free; FCS.Free; end; procedure TMyThread.DoWorkOnItem( Item : TObject ); begin // Hier irgendetwas mit dem Item machen end; procedure TMyThread.Execute; var LItem : TObject; begin inherited; // die übliche Schleife ... while not Terminated do begin // Warten, bis es etwas zu arbeiten gibt FEvent.WaitFor; // Wenn der Event gefeuert wurde, prüfen wir mal ob ... if not Terminated then begin // Item aus der ToDoListe holen LItem := GetItem; try // Mit dem Item arbeiten DoWorkOnItem( LItem ); finally // Item-Instanz freigeben LItem.Free; end; end; end; end; function TMyThread.GetItem : TObject; begin FCS.Enter; try // Item aus der ToDoListe entnehmen Result := FToDoList.Extract( FToDoList.First ); // Wenn dort nocht Items enthalten sind, dann setzen wir den Event auch wieder if FToDoList.Count > 0 then FEvent.SetEvent; finally FCS.Leave; end; end; {$IFDEF USE_TERMINATEDSET} procedure TMyThread.TerminatedSet; begin inherited; // Wenn Terminted, dann braucht hier keiner mehr warten FEvent.SetEvent; end; {$ENDIF} procedure TMyThread.WorkOnItem( Item : TObject ); begin FCS.Enter; try // Item in die ToDoListe einfügen FToDoList.Add( Item ); // und per Event den Thread aufwecken FEvent.SetEvent; finally FCS.Enter; end; end; end. |
AW: Arbeiten mit TThreadList
Ok. Vielen Dank für dein Beispiel. Die BeforeDestruction werd' ich einbauen.
Delphi-Quellcode:
destructor TD2007Thread.Destroy;
begin if (FThreadID <> 0) and not FFinished then begin Terminate; if FCreateSuspended then Resume; WaitFor; end; RemoveQueuedEvents(Self, nil); {$IFDEF MSWINDOWS} if FHandle <> 0 then CloseHandle(FHandle); {$ENDIF} {$IFDEF LINUX} // This final check is to ensure that even if the thread was never waited on // its resources will be freed. if FThreadID <> 0 then pthread_detach(FThreadID); sem_destroy(FCreateSuspendedSem); {$ENDIF} inherited Destroy; FFatalException.Free; RemoveThread; end; |
AW: Arbeiten mit TThreadList
Ein Problem sehe ich hier im Destructor
Delphi-Quellcode:
Das Problem kann hier auftauchen:
destructor TD2007Thread.Destroy;
begin if (FThreadID <> 0) and not FFinished then begin Terminate; if FCreateSuspended then Resume; WaitFor; // Diese Stelle ist problematisch end;
Delphi-Quellcode:
Denn nun wird zwar im Destructor korrekterweise das TThread.Resume aufgerufen, allerdings dauert es eine geraume Zeit bis der Thread wirklich losläuft und somit auch TThread.WaitFor eine Chance hat darauf zu warten bis der Thread wirklich wieder beendet ist. Hier kann es also passieren, dass nun nicht gewartet wird, der Destructor weiter ausgeführt wird und irgendwann der Thread eigentlich erst losläuft.
var
LThread : TThread; begin LThread.Create( True ); LThread.Free; end; In neueren Delphi-Versionen wird genau nach dem
Delphi-Quellcode:
noch in Kombination mit TThread.Yield (ab XE) gewartet bis der Thread auch wirklich angelaufen ist.
Resume
Delphi-Quellcode:
if FCreateSuspended or FSuspended then
Resume; while not FStarted do Yield; WaitFor; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 20:37 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