![]() |
Delphi-Version: XE2
Thread + Timer, Einbinden von TimerThread.pas
Hallo Sir Rufo,
kannst du mir mal kurz die Anwendung deiner Unit TimerThread.pas ![]() Ich habe bisher Threads noch nicht verwendet, zwar hier Vieles gelesen, aber auch nicht alles verstanden. Bin mir auch nicht sicher ob die Unit für mein Prog passend ist. Ich möchte in meinem Prog zyklisch z.B. alle 5 Minuten aus einer Textdatei Werte holen, die dann vorhalten/aufbereiten um diese dann im Prog zu verwenden. Mir geht's jetzt mehr ums Prinzip nicht um den Feinschliff. Vielen Dank im voraus. Starten/Stoppen tue ich den Ablauf über das Property .Enabled; Den zyklisch auszuführenden Code habe ich bei .ExecuteTimed plaziert. Muss ich da auch noch Terminated abfragen? Soweit scheint es zu funktionieren, wenn ich stoppe wird aber noch ein Durchlauf gemacht. Gruß MarLe |
AW: Thread + Timer, Einbinden von TimerThread.pas
Jupp, dass nach dem
Delphi-Quellcode:
noch einmal
Enabled := False
Delphi-Quellcode:
aufgerufen wird, liegt daran, dass ich vergessen habe im Setter von
ExecuteTimed
Delphi-Quellcode:
den Event auszulösen.
Enabled
Habe ich jetzt mal geändert und zusätzlich dahingehend erweitert, dass der Event nur bei einer echten Änderung auslöst (
Delphi-Quellcode:
und
Interval
Delphi-Quellcode:
).
Enabled
Ein kleiner Fehler war auch noch im
Delphi-Quellcode:
, da wurde der Wert
Execute
Delphi-Quellcode:
nicht benutzt :oops:
LInterval
Zur Verwendung: Am sinnvollsten ist es sich von dieser Klasse eine neue Klasse abzuleiten.
Delphi-Quellcode:
Hier die korrigierte Version der Unit:
unit MyTimerThread;
interface uses TimerThread; type TMyTimerThread = class( TTimerThread ) protected procedure ExecuteTimed; override; end; implementation procedure TMyTimerThread.ExecuteTimed; begin // hier jetzt der eigene Code rein end; end.
Delphi-Quellcode:
unit TimerThread;
interface uses System.Classes, System.SyncObjs; const TIMERTHREAD_INTERVAL_DEFAULT = 1000; TIMERTHREAD_ENABLED_DEFAULT = True; type TTimerThread = class( TThread ) private FCS : TCriticalSection; FEvent : TEvent; FInterval : Cardinal; FEnabled : Boolean; procedure SetInterval( const Value : Cardinal ); function GetInterval : Cardinal; procedure SetEnabled( const Value : Boolean ); function GetEnabled : Boolean; protected procedure Execute; override; final; procedure ExecuteTimed; virtual; // ACHTUNG! Das gibt es erst ab Delphi XE2 procedure TerminatedSet; override; public constructor Create; destructor Destroy; override; property Interval : Cardinal read GetInterval write SetInterval default TIMERTHREAD_INTERVAL_DEFAULT; property Enabled : Boolean read GetEnabled write SetEnabled default TIMERTHREAD_ENABLED_DEFAULT; end; implementation { TTimerThread } constructor TTimerThread.Create; begin FCS := TCriticalSection.Create; FEvent := TEvent.Create( nil, False, False, '' ); inherited Create( False ); FInterval := TIMERTHREAD_INTERVAL_DEFAULT; FEnabled := TIMERTHREAD_ENABLED_DEFAULT; end; destructor TTimerThread.Destroy; begin inherited; FEvent.Free; FCS.Free; end; procedure TTimerThread.Execute; var LInterval : Cardinal; begin inherited; while not Terminated do begin if Enabled then LInterval := Interval else LInterval := INFINITE; if FEvent.WaitFor( LInterval ) = TWaitResult.wrTimeout then ExecuteTimed; end; end; procedure TTimerThread.ExecuteTimed; begin end; function TTimerThread.GetEnabled : Boolean; begin FCS.Enter; try Result := FEnabled; finally FCS.Leave; end; end; function TTimerThread.GetInterval : Cardinal; begin FCS.Enter; try Result := FInterval; finally FCS.Leave; end; end; procedure TTimerThread.SetEnabled( const Value : Boolean ); begin FCS.Enter; try if Value <> FEnabled then begin FEnabled := Value; FEvent.SetEvent; end; finally FCS.Leave; end; end; procedure TTimerThread.SetInterval( const Value : Cardinal ); begin FCS.Enter; try if Value <> FInterval then begin FInterval := Value; FEvent.SetEvent; end; finally FCS.Leave; end; end; procedure TTimerThread.TerminatedSet; begin inherited; FEvent.SetEvent; end; end. |
AW: Thread + Timer, Einbinden von TimerThread.pas
Guten Morgen,
den Fehler im Execute hatte ich beim "Studieren" der Unit auch schon korrigiert. Die anderen Änderungen habe ich umgesetzt und jetzt schaut's gut aus, Danke für die Unterstützung. Noch eine Frage zu der Vorbelegung TIMERTHREAD_ENABLED_DEFAULT = True; Müsste der Ablauf nach einem .Create nicht gleich starten, bei mir startet es erst nach einem zusätzlichen .Enabled := True; Gruß MarLe |
AW: Thread + Timer, Einbinden von TimerThread.pas
Jo, stimmet, da fehlte im
Delphi-Quellcode:
doch tatsächlich
Create
Delphi-Quellcode:
:)
FEnabled := TIMERTHREAD_ENABLED_DEFAULT;
Ist jetzt auch drin ;) |
AW: Thread + Timer, Einbinden von TimerThread.pas
Jetzt habe ich noch zwei Verständnisfragen:
Soll im .Execute nicht auf FEnabled abgefragt werden, oder ist das egal? Ich habe jetzt mal zum Testen auch den Thread gestoppt, mit .Terminate; Wie starte ich diesen wieder mit .Start; (was macht .ResetEvent) hat so nicht funktioniert ? Gruß MarLe |
AW: Thread + Timer, Einbinden von TimerThread.pas
Im
Delphi-Quellcode:
wird doch
Execute
Delphi-Quellcode:
überprüft
Enabled
Delphi-Quellcode:
Noch mehr prüfen? Worauf und wozu? ;)
if Enabled then
LInterval := Interval else LInterval := INFINITE; Wenn ein Thread mit
Delphi-Quellcode:
beendet wurde, dann ist der durch mit dem Thema und du musst den Thread komplett neu erstellen. Ein Neustart nach
Terminate
Delphi-Quellcode:
ist da nicht vorgesehen.
Terminate
Wo ist denn da
Delphi-Quellcode:
in dem Source? :gruebel:
ResetEvent
|
AW: Thread + Timer, Einbinden von TimerThread.pas
Ich kann ja auch auf FEnabled (statt Enabled) abfragen, funktioniert auch, deshalb die Frage.
Die zweite Frage hat sich erledigt, da habe ich woanders was von ResetEvents gelesen, aber falsch aufgefasst. Gruß MarLe |
AW: Thread + Timer, Einbinden von TimerThread.pas
Zitat:
Delphi-Quellcode:
Die
FCS.Enter:
try if FEnabled then LInterval := FInterval else LInterval := INFINITE; finally FCS.Leave; end; ![]() Bei Threads gibt es neben dem es funktioniert (kompiliert und ist auch mal gelaufen) auch noch ein es funktioniert gesichert immer ;) |
AW: Thread + Timer, Einbinden von TimerThread.pas
Ok alles klar, so wie du es beschrieben hast habe ich es auch gemeint, ist quasi egal ob ich auf FEnabled oder Enabled abfrage.
"es funktioniert gesichert immer" ist mir natürlich am liebesten! Gruß Marle |
AW: Thread + Timer, Einbinden von TimerThread.pas
Weile ich ein anderes Thema damit nicht belasten möchte hier die Frage.
Ist dieser TimerThread mit Warte-Events nicht anderes als ein normaler TThread mit einer while-Schleife und einem Sleep(1234) im Execute-Teil? Nur.. mit Events eben.. und nicht mit Sleep? Wie sähe das hier mit dem TimerThread aus?
Delphi-Quellcode:
procedure TMyNormalThread.Execute;
begin while not Terminated do begin Sleep(1000); if 1 = 2 then // viel zu tun end; end; |
AW: Thread + Timer, Einbinden von TimerThread.pas
Zitat:
|
AW: Thread + Timer, Einbinden von TimerThread.pas
Angenommen ich möchte einen Thread 5 Stunden schlafen legen und er soll nach diesen 5 Stunden alleine aufwachen.
Wie bewerkstellige ich das ohne Sleep? |
AW: Thread + Timer, Einbinden von TimerThread.pas
Delphi-Quellcode:
5 Stunden * 60 Minuten * 60 Sekunden * 1000 Millisekunden?
Interval := 5 * 60 * 60 * 1000;
Und wenn er dann wach wird, Interval auf den dann gewünschten Zeitraum setzen? |
AW: Thread + Timer, Einbinden von TimerThread.pas
Zitat:
Ich würde mir in so einen Fall einen Timeout von X Minuten setzen und dann immer in einer Liste nachschauen ob ein Event ansteht. Beispiel: ThreadTimeout auf 1h... Wenn der nächste Termin < 1h ist setze ich den Timeout auf "Restzeit" dann habe ich einen Event der genau feuert. Das gleiche, wenn ein neuer Event in die Liste kommt. |
AW: Thread + Timer, Einbinden von TimerThread.pas
Ich verstehe ehrlich gesagt den Sinn von Events in diesem Falle nicht.
Warum nicht einfach einen Sleep und fertig? Wie sieht denn dieses Beispiel mit der Event-Thread-Ableitung aus? Vielleicht kapiere ich es dann. Die 5 Stunden waren nur ein Beispiel.
Delphi-Quellcode:
procedure TMyNormalThread.Execute;
begin while not Terminated do begin Sleep(1000); if 1 = 2 then // viel zu tun end; end; |
AW: Thread + Timer, Einbinden von TimerThread.pas
Zitat:
|
AW: Thread + Timer, Einbinden von TimerThread.pas
Das verstehe ich jetzt. Könntest du das Minimalbeispiel oben eventuell umändern? Vielleicht verstehe ich dann auch den Rest und die Handhabe generell.
|
AW: Thread + Timer, Einbinden von TimerThread.pas
Siehe hier:
![]() Du weist Interval die gewünschte Zeitspanne zu. Die wird dann im Execute von selbst berücksichtigt. Ein Beispiel dazu: ![]() Also grob geraten, da ungetestet:
Delphi-Quellcode:
procedure TMyNormalThread.Execute;
var res : TWaitResult; begin while not Terminated do begin // Sleep(1000); res := FEvent.WaitFor(FInterval); // also z.B. 1000 // if 1 = 2 then // viel zu tun // end; case res of wrSignaled : if 1 = 2 then begin // viel zu tun end; wrTimeout : ; wrAbandoned : Terminate; wrError : { Fehler loggen }; end; end; end; |
AW: Thread + Timer, Einbinden von TimerThread.pas
Ich probiere das gleich mal aus! Danke.
Habe mich anders entschieden. Ist mir den Aufwand nicht wert. Ich belasse alles so wie es aktuell ist. Mit Sleep fahre ich gut. Außerdem kapier ich das eh nicht. |
AW: Thread + Timer, Einbinden von TimerThread.pas
Mich intertessiert aber trotzdem noch wie man so einen Thread aufweckt und schlafen legt.
Und wie man auf Terminate reagiert und ob man in WaitFor festhängt. Und woher kommt das Ergebnis von FEvent.WaitFor? Muss man das von Außen setzen? Am wichtigsten ist aber die Frage was überhaupt der Unterschied ist
Delphi-Quellcode:
ist es nicht vollkommen egal, ob WaitFor oder Sleep? Nach 1000 ms gehts doch eh weiter und CPU-Zeit wird verbraten.
// Sleep(1000);
res := FEvent.WaitFor(FInterval); // also z.B. 1000 // was zu tun |
AW: Thread + Timer, Einbinden von TimerThread.pas
Zitat:
Der Thread "schläft" im WaitFor(TimeOut) für TimeOut ms. Ich sage mal in dieser Zeit wird "NULL" CPU-Zeit verbraucht. Aufgeweckt wird der Thread entweder wenn TimeOut aufgetreten ist oder wenn mit SetEvent der Event gesetzt wird (extern oder ggf. im Destructor) Also bei einem FThreadTimer.Free rufst Du im destructor Setevent auf, damit die Execute procedure aus dem Waitfor raus kommt und gut ist es. Gibt da noch einen ![]() Mavarik |
AW: Thread + Timer, Einbinden von TimerThread.pas
Zitat:
Das bedeutet zwangsweise, dass ein langes Warten bei einem Sleep umgangen wird wenn der Thread beendet wird. |
AW: Thread + Timer, Einbinden von TimerThread.pas
Zitat:
|
AW: Thread + Timer, Einbinden von TimerThread.pas
Delphi-Quellcode:
Angenommen ich würde mein Programm nun beenden. Dann müsste ich noch so lange warten, bis Sleep() vorbei ist.
procedure TNormalThread.Execute;
begin Sleep(60000); end; Mit den Events habe ich das jetzt so verstanden, dass das WaitFor unterbrochen wird, sobald ich das Signal dazu an den Thread schicke (z.B. im OnClose). |
AW: Thread + Timer, Einbinden von TimerThread.pas
Zitat:
|
AW: Thread + Timer, Einbinden von TimerThread.pas
100 Gummipunkte für meinen Hohlkopf!
|
AW: Thread + Timer, Einbinden von TimerThread.pas
Leider -200 Gummipunkte für mich. Ich glaube ich bin zu dämlich dafür die Ableitung aus Beitrag 2 zu nutzen :pale:
Delphi-Quellcode:
var TestThread: TTestThread;
procedure TForm1.Button2Click(Sender: TObject); begin TestThread:= TestThread.Create; TestThread.Interval := 60000; end;
Delphi-Quellcode:
unit Unit3;
interface uses System.Classes, System.Classes, System.SyncObjs, uCustomThread, Dialogs; type TTestThread = class(TCustomThread) // TCustomThread ist die Unit aus Beitrag 2 private {Private-Deklarationen} protected procedure Execute; override; end; implementation {TTestThread} procedure TTestThread.Execute; var res: TWaitResult; begin while not Terminated do begin // Sleep(1000); res := FEvent.WaitFor(FInterval); // also z.B. 1000 // An FEvent komme ich nicht dran. An FInterval natürlich auch nicht. Nur die Frage ist wieso. // if 1 = 2 then // viel zu tun // end; case res of wrSignaled: TThread.Synchronize(nil, procedure begin showmessage('Signaled'); end); wrTimeout: ; wrAbandoned: Terminate; wrError: {Fehler loggen} ; end; end; end; end. |
AW: Thread + Timer, Einbinden von TimerThread.pas
Du musst
executeTimed überschreiben. Eigentlich sollte der Compiler meckern da Execute als final deklariert ist |
AW: Thread + Timer, Einbinden von TimerThread.pas
Damit das mit dem Event auch klappt, habe ich folgende Zeile in der Hauptunit (Beitrag 2) eingefügt. Ansonsten kann ich Event in einer Ableitung nicht sehen.
Delphi-Quellcode:
ExecuteTimed also. Ok. Ich habe währenddessen ein bisschen rumprobiert um zu sehen wie der Thread reagiert je nachdem was ich mache.
public
property Event: TEvent read FEvent write FEvent; Mit dem Event-Property funktioniert dann auch das hier
Delphi-Quellcode:
Was genau wrSignaled macht habe ich aber noch nicht herausgefunden.
procedure TTestThread.ExecuteTimed;
var res: TWaitResult; begin while not Terminated do begin res := Event.WaitFor(Interval); // also z.B. 1000 case res of wrSignaled: TThread.Synchronize(nil, procedure begin showmessage('Signaled'); // Wird beispielsweise aufgerufen, wenn ich außen <Thread-Instanz>.Terminate; ausführe end); wrTimeout: TThread.Synchronize(nil, procedure begin showmessage('Timeout'); // Wird immer aufgerufen, wenn oben das WaitFor vorbei ist AUßER es gab vorher wrSignaled, wrAbandoned oder wrError. end); wrAbandoned: Terminate; wrError: {Fehler loggen} ; end; end; TThread.Synchronize(nil, procedure begin showmessage('Thread Ende'); end); end; |
AW: Thread + Timer, Einbinden von TimerThread.pas
jetzt hast du 2 mal eine Schleife die auf Terminated wartet.
Im execute und im ExecuteTimed. Vergiss doch einfach den Event im ExecuteTimed, oder noch besser versuche zu verstehen was Deine ThreadKlasse wirklich macht.... |
AW: Thread + Timer, Einbinden von TimerThread.pas
Ich denke ich habs jetzt raus.
Das while in override ExecuteTimed muss natürlich raus weil das TCustomThread.Execute; in einer Schleife ja auch immer ExecuteTimed aufruft. Nun funktioniert auch Enabled richtig. Zitat:
Dann wird Beitrag 18 vermutlich falsch sein ![]() |
AW: Thread + Timer, Einbinden von TimerThread.pas
Zitat:
Du machst eine Ableitung, überschreibst ExecuteTimed und gut ist. Im alles andere kümmert sich die Thread-Klasse |
AW: Thread + Timer, Einbinden von TimerThread.pas
Zitat:
Delphi-Quellcode:
Und in TCustomThread.Execute
public
property Event: TEvent read FEvent write FEvent; // Damit man Event von Außen überhaupt sieht
Delphi-Quellcode:
Nur bin ich mir bei der letzten Zeile nicht so sicher, ob das OK ist.
// Aus
if FEvent.WaitFor(LInterval) = TWaitResult.wrTimeout then // wird if FEvent.WaitFor(LInterval) in [TWaitResult.wrTimeout, TWaitResult.wrSignaled] then Von Außen kann man dann aufrufen
Delphi-Quellcode:
TestThread.Event.SetEvent;
|
AW: Thread + Timer, Einbinden von TimerThread.pas
Delphi-Quellcode:
Das ist Ok, aber den Event ohne Setter und Getter sichtbar machen nicht.
if FEvent.WaitFor(LInterval) in [TWaitResult.wrTimeout, TWaitResult.wrSignaled] then
Lass das weg und benutze im code doch einfach das Enabled Property, da wird der Event auch ausgelöst. (Threadsafe) |
AW: Thread + Timer, Einbinden von TimerThread.pas
Wenn ich aber das Property Enabled verwende, hat das nicht denselben Zweck.
SetEvent bräuchte ich, um sofort aus dem WaitFor rauszukommen und alles weitere in ExecuteTimed auszuführen. Enabled auf False stellen würde WaitFor für INFINITE lang im Kreis rennen lassen. |
AW: Thread + Timer, Einbinden von TimerThread.pas
Zitat:
|
AW: Thread + Timer, Einbinden von TimerThread.pas
Zum Glück seid ihr keine Lehrer. Denn sonst hättet ihr mit circa 90% solcher Idioten zu tun wie ich :thumb:
|
AW: Thread + Timer, Einbinden von TimerThread.pas
Zitat:
|
AW: Thread + Timer, Einbinden von TimerThread.pas
Eine Frage habe ich noch zu diesem ExecuteTimes.
In ExecuteTimed ist eine while-Schleife. Was aber, wenn ich eine Aufgabe habe die von Anfang bis Ende nur einmal durchlaufen soll und wo ich keine Schleife benötige? Würde man in diesem Falle den Thread dafür erzeugen und sofort terminieren? |
AW: Thread + Timer, Einbinden von TimerThread.pas
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:34 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