![]() |
Thread soll eine Minute warten: Sleep oder Timer?
Hallo zusammen,
nachdem ich mich jahrelang um Threads herumdrücken konnte (außer vor ein paar Jahren in der Uni in der Theorie), komme ich nun an dem Thema wohl nicht mehr vorbei, da ich in einem Projekt eine periodische Datenbank-Abfrage implementieren möchte, die den Benutzer aber nicht weiter stören soll. Konkret soll also einmal pro Minute eine MSSQL-Datenbank abgefragt werden und nur, falls dort bestimmte Informationen vorliegen, soll der Benutzer bei seiner eigentlichen Arbeit gestört werden. Ich habe nun noch ein Konstruktions- bzw. Verständnisproblem. Wie schaffe ich es am elegantesten, dass der Thread einerseits eine Minute lang nix macht, nachdem die Datenbank abgefragt wurde, andererseits aber dennoch ziemlich schnell terminiert wird, sobald der Hauptthread das wünscht (weil das Programm beendet wird oder aus anderen Gründen). Denn: Wenn man den Thread mit "sleep" Schlafen schickt, bekommt er von einem Terminate ja erst mal nix mehr mit. Ich hab hier in der DP schon ein bisschen geschmökert, was mir auch die eine oder andere Idee gebracht hat, aber so richtig sicher bin ich mir noch nicht, was nun die "ultimative" Lösung ist (obwohl ich jetzt eher erwartet hätte, dass das ein Standard-Problem wäre - nicht richtig gesucht?) :) Wobei ich jetzt beim Tippen das Gefühl bekommen habe, auf einem guten Weg zu sein... Ich hab mir mal ein kleines Beispiel gebastelt, das mit Datenbanken noch gar nix macht (ist ja auch nicht mein eigentliches Problem... hoffentlich) und mit dem sich die Überlegungen ganz gut zeigen lassen: Die Thread-Klasse:
Delphi-Quellcode:
(damit ich beim Testen nicht ewig warten muss, legt sich der Thread hier nur 10 Sekunden Schlafen)
unit thread;
interface uses Classes, SyncObjs; type TCalcThread = class(TThread) private { Private-Deklarationen } resultText: string; protected procedure Execute; override; procedure ShowResult; public calcValue: integer; constructor Create(CreateSuspended: Boolean); end; var CriticalParams : TCriticalSection; implementation uses main, sysUtils; { Wichtig: Methoden und Eigenschaften von Objekten in visuellen Komponenten dürfen nur in einer Methode namens Synchronize aufgerufen werden, z.B. Synchronize(UpdateCaption); und UpdateCaption könnte folgendermaßen aussehen: procedure TCalcThread.UpdateCaption; begin Form1.Caption := 'Aktualisiert in einem Thread'; end; } { TCalcThread } procedure TCalcThread.ShowResult; begin thread1_main.LabelResult.Caption:=resultText; end; procedure TCalcThread.Execute; var myValue: integer; begin { Thread-Code hier einfügen } while not terminated do begin CriticalParams.Acquire; myValue:=calcValue; CriticalParams.Release; if myValue=-1 then resultText:='nix gesetzt.' else resultText:=intToStr(myValue*80); Synchronize(ShowResult); sleep(10000); end; end; constructor TCalcThread.Create(CreateSuspended: Boolean); begin inherited; calcValue:=-1; end; end. Im Hauptprogramm gibt es nun einen Button, mit dem man den Thread beenden kann:
Delphi-Quellcode:
Das Problem sollte klar sein: Es kann bis zu 10 Sekunden dauern, bis der Thread nach dem Klick auf den Button wirklich beendet wird. Am einfachsten wäre es ja, wenn man das sleep irgendwie von außen unterbrechen könnte - also das Hauptprogramm schickt einen Weckruf, der den Wecker einfach etwas vorstellt, aber sowas habe ich nicht gefunden. Also wird man wohl drumrum basteln müssen...
procedure Tthread1_main.ButtonStopThreadClick(Sender: TObject);
begin theThread.Terminate; // warten bis der Thread beendet wurde... theThread.WaitFor; messageDlg('Der Thread ist durch.',mtInformation,[mbOk],0); theThread.Free; theThread:=nil; end; Am einfachsten wäre es wahrscheinlich, wenn man nicht 10 Sekunden wartet, sondern 10 mal eine Sekunde, so dass aus
Delphi-Quellcode:
ein
sleep(10000);
Delphi-Quellcode:
würde. Der Hauptthread bekäme dann wohl viel schneller eine Rückmeldung, aber irgendwie fühlen sich diese ständigen Wechsel in den Thread nicht so richtig gut an. Hat irgendwie was vom aktiven Warten.
i:=0
while (not terminated) and (i<10) do begin sleep(1000); inc(i); end; Als andere Alternative wollte ich jetzt eigentlich vorschlagen, dass ein Timer den Thread periodisch freigibt und neu erzeugt, aber beim Schreiben kam mir jetzt gerade der Gedanke, dass ein Timer wahrscheinlich ein guter Ansatz ist, aber ein Suspend/Resume wahrscheinlich besser, als das Dingen zu zerstören und neu anzulegen. Das heißt, die Execute-Methode vom Thread sähe dann so aus:
Delphi-Quellcode:
Und im Hauptthread gibt es dann ein Timer-Ereignis und eine Änderung beim Klicken auf den Button:
procedure TCalcThread.Execute;
var myValue: integer; begin { Thread-Code hier einfügen } while not terminated do begin CriticalParams.Acquire; myValue:=calcValue; CriticalParams.Release; if myValue=-1 then resultText:='nix gesetzt.' else resultText:=intToStr(myValue*80); Synchronize(ShowResult); Suspend; end; end;
Delphi-Quellcode:
Ist da irgendein Denkfehler drin oder ist das die "ultimative Lösung", nach der ich gesucht habe? Mir kommt sie gerade eigentlich ganz gut vor. :dancer:
procedure Tthread1_main.TimerThreadTimer(Sender: TObject);
begin theThread.Resume; end; procedure Tthread1_main.ButtonStopThreadClick(Sender: TObject); begin TimerThread.Enabled:=false; theThread.Terminate; theThread.Resume; // damit das Dingen auch merkt, dass er beendet wurde... // warten bis der Thread beendet wurde... theThread.WaitFor; messageDlg('Der Thread ist durch.',mtInformation,[mbOk],0); theThread.Free; theThread:=nil; end; Ich bin auf eure Rückmeldungen gespannt. Danke und bis denn Bbommel |
Re: Thread soll eine Minute warten: Sleep oder Timer?
In einem Thread ist sleep nutzbar (nur auf Terminate sollte man achten). Besser ist waitforsingleobject mit einer Zeitangabe.
Edit: Waitforsingleobject irgendwie so:
Delphi-Quellcode:
constructor Tmythread.Create(CreateSuspended: Boolean);
begin inherited Create(false); FEvent:=TEvent.Create(nil,true,false,''); end; destructor Tmythread.Destroy; begin FEvent.Free; inherited; end; procedure Tmythread.DoSomething; begin //abstract ;-) end; procedure Tmythread.Terminate; begin inherited; FEvent.SetEvent; end; procedure Tmythread.execute; begin while not terminated do begin DoSomething; FEvent.ResetEvent; FEvent.WaitFor(10000); end; end; |
Re: Thread soll eine Minute warten: Sleep oder Timer?
Denke mal, dass das auch mit GetTickCount gehen sollte. AFAIK ist das Threadsafe.
|
Re: Thread soll eine Minute warten: Sleep oder Timer?
Hallo,
wie wärs denn mit KISS (keep it simple and stupid): Das Programm wartet nur darauf, beendet zu werden. Die Funktionalität wird im Timer Service erledigt, falls was vorliegt. Weder Thread noch sleep nötig. Gruss Reinhard |
Re: Thread soll eine Minute warten: Sleep oder Timer?
ich würde vielleicht nich direkt ein 60-Sekunden-Sleep einbauen, denn so kannst du den Thread nicht mehr so schön/schnell beenden. (vorallem wenn das Programm beendet werden soll ... wär ja blöd, wenn du dann im schlimmsten Fall noch 'ne Minute gewartet werden müßte)
CPU-schonend und unterbrechbar wäre z.B. sowas:
Delphi-Quellcode:
oder was mit WaitFor... wie sirius grad nachtrug
Var T: LongWord;
ThreadSollBeendetWerden: Boolean; ... T := GetTickCount; While (GetTockCount - T < 60000) and not ThreadSollBeendetWerden do Sleep(200); |
Re: Thread soll eine Minute warten: Sleep oder Timer?
Zitat:
Delphi-Quellcode:
durch ein
sleep(10000)
Delphi-Quellcode:
ersetzen?
waitForSingleObject(Handle,10000);
So ganz schlau werde ich daraus noch nicht - jetzt bei diesem Versuch wartet der Thread ja, bis er selbst signalisiert (also beendet, wenn ich das richtig verstanden habe) wird. Aber das bringt mich offenbar nicht so richtig weiter oder kann man dieses Signal auch manuell senden? Oder muss ich da einen extra Handler für erstellen? Ich hatte zu dem Thema auch schon im Thread-Tutorial von Luckie was gelesen, aber dort werden die wait-Funktionen ja gerade auch außerhalb des Threads eingesetzt, was ja auf mein Problem nicht so recht zutrifft. Bis denn Bommel |
Re: Thread soll eine Minute warten: Sleep oder Timer?
Ähm, das zuerst gedachte Edit oben sollte hier als Antwort hin.
|
Re: Thread soll eine Minute warten: Sleep oder Timer?
Zitat:
Zitat:
Nur zum Verständnis noch mal nachgefragt: Das ist ja eigentlich der gleiche Ansatz, wie ich ihn mit dem mehrfachen Hochzählen von i und dazwischen einem kürzeren sleep hatte, nur über den TickCount eleganter, weil die 60 Sekunden besser eingehalten werden können, richtig? Mein Problem mit dem Ansatz ist nur, dass es sich komisch "anfühlt", wenn andauernd ein Kontextwechsel gemacht wird, obwohl eigentlich gar nichts zu tun ist. Ich kann nur überhaupt nicht einschätzen, ob sowas auch tatsächlich nach bemerkbar ist oder ob ich mir dann den Kopf über ein eher akademisches Problem zerbreche. :) Bis denn Bommel |
Re: Thread soll eine Minute warten: Sleep oder Timer?
Zitat:
Bis denn Bommel |
Re: Thread soll eine Minute warten: Sleep oder Timer?
Zitat:
Ein simpler timer reicht doch dann völlig. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 00:51 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