2009-07-02 6 views
6

Überprüfen Sie einfach, ob es eine Best Practice beim Schreiben eines Windows-Dienstes gibt.Windows-Dienst: Arbeiten zu bestimmten Zeiten (Delphi)

Der Dienst (Single-Thread) muss in bestimmten Zeitintervallen arbeiten, jetzt kann ich nur denken:

  1. Verwenden sleep(), dann in einer Schleife, die Zeit überprüfen?
  2. Verwenden Sie einen TTimer?

Irgendwelche Ratschläge?

Antwort

10

Muss dies ein Service sein? Könnten Sie vielleicht eine geplante Aufgabe in Windows einrichten?

+0

Die erste Frage kam mir auch in den Sinn. http://weblogs.asp.net/jgalloway/archive/2005/10/24/428303.aspx – ilitirit

+0

Eine interessante Lektüre. Es hat sehr gültige Punkte und einige der Kommentare. Ich mag dieses Zitat besonders: "Es ist besser, einen vorhandenen Dienst zu verwenden, als einen eigenen zu rollen, der das Gleiche tut. Aber ... Windows Task Scheduler saugt." Also, Taskplaner sollte die erste Sache sein, zu versuchen, und das Schreiben eines Dienstes sollte nur in extremen Fällen erfolgen. Aber diese Fälle existieren, ich schrieb einen Terminplanungsdienst nur, nachdem ich lange gegen den Aufgabenplanungsdienst gekämpft hatte und schließlich akzeptieren musste, dass er einfach nicht vielseitig genug war. – mghie

3

Ich würde den Schlaf nutzen.

Beide Optionen haben keine genaue Zeitgarantie, aber der Schlaf gibt Ressourcen an andere Prozesse zurück.

+1

Timer-Nachrichten würden mit einem Meldungsschleife verwendet werden, und GetMessage() nicht blockiert, wenn es keine Nachrichten in der Warteschlange sind. Ressourcen werden nicht vergeudet. – mghie

13

Es ist nicht wirklich wichtig, dass Ihr Dienst single-threaded ist, als ein Dienst seinen Code in anderen Thread Kontexte aufgerufen hat immer:

  • Der Service Manager wird starten, stoppen, anhalten und fortsetzen die Ausführung des Dienstes und fordern Sie den aktuellen Dienststatus an.

  • Der Dienst selbst wird über mindestens einen Thread verfügen, der die eigentliche Arbeit erledigt. Er muss auf die Anforderungen des Dienst-Managers reagieren, den Status der Dienstausführung wie angefordert ändern und die angeforderten Informationen zurückgeben. Ein Dienst muss in relativ kurzer Zeit auf Anforderungen vom Service Manager reagieren. Andernfalls wird der Dienst angehalten und beendet. Aus diesem Grund ist es möglicherweise besser, wenn mehr als ein Dienst-Thread vorhanden ist, wenn der Dienst Code lange ausführen oder blockieren kann.

Ob Sleep() oder Timer-Nachrichten auch abhängig von der Verfügbarkeit einer Nachricht Pumpe in den Service-Threads nicht zu verwenden. Wenn Sie keine Nachrichtenpumpe haben, sollten Sie Sleep() oder Timer-Rückrufe verwenden. Wenn Sie trotzdem eine Nachricht pumpen müssen, weil Sie mit anderen Prozessen oder Threads über Windows-Nachrichten kommunizieren müssen, oder Sie müssen OLE-Sachen machen, dann ist es am einfachsten, Timer-Nachrichten zu verwenden.

Vor ein paar Jahren schrieb ich einen Dienst für zeitgesteuerte Ausführung von Aufgaben, ähnlich wie Windows at oder Unix cron Funktionalität. Es verwendet nicht viel von der VCL, nur einige Basisklassen. Die Run() Methode des Dienstes sieht wie folgt aus:

procedure TScheduleService.Run; 
var 
    RunState: TServiceRunState; 
begin 
    while TRUE do begin 
    RunState := GetServiceRunState; 
    if (RunState = srsStopped) or (fEvent = nil) then 
     break; 
    if RunState = srsRunning then begin 
     PeriodicWork; 
     Sleep(500); 
    end else 
     fEvent.WaitFor(3000); 
    Lock; 
    if fServiceRunStateWanted <> srsNone then begin 
     fServiceRunState := fServiceRunStateWanted; 
     fServiceRunStateWanted := srsNone; 
    end; 
    Unlock; 
    end; 
end; 

Dies verwendet Sleep() in einer Schleife, aber eine Lösung

while integer(GetMessage(Msg, HWND(0), 0, 0)) > 0 do begin 
    TranslateMessage(Msg); 
    DispatchMessage(Msg); 
end; 

würde funktionieren genauso gut mit, und Dann könnten Windows-Timer-Nachrichten verwendet werden.

+0

Ich würde die Nachrichtenschleife vermeiden, wenn Sie sie wirklich brauchen. Die erste Schleife ist grundsätzlich was ich tue, obwohl in einem Thread. – mj2008

3

TTimer ist nicht threadsicher. Wenn Sie einen TTimer-ähnlichen Ansatz in einem Thread verwenden müssen, würde ich TDSiTimer von DSiWin32 vorschlagen.

2

Niemals einen TTimer in einem Dienst verwenden, er wird sich nicht immer so verhalten, wie Sie es erwarten, und er ist nicht threadsicher.

Innerhalb eines Dienstes verwende ich immer meine eigenen Zeitintervallvariablen und schalte zwischen Taskausführungszeiten ein. Um den Dienst reaktionsfähig zu halten, schlafe ich für kurze Zeiträume, typischerweise 1-2000 ms, und verarbeite dann Nachrichten, bevor ich mein Intervall überprüfe, um zu wissen, ob es Zeit ist, die 'Aufgabe' auszuführen. Wenn es noch nicht an der Zeit ist, gehen Sie zurück in den Schlafmodus und überprüfen Sie danach erneut - in einer Schleife. Auf diese Weise geben Sie Ressourcen zurück, können aber auch auf Benutzereingaben reagieren (Stopp, Pause), bevor die nächste Aufgabe ausgeführt wird.

0

ich so etwas wie dies in einem Service verwenden alwasy:

unit uCopy; 

interface 

uses 
    Windows, Messages,.......; 

procedure MyTimerProc(hWindow : HWND; uMsg : cardinal; idEvent : cardinal; dwTime : DWORD); stdcall; 

type 
    TFileCopy= class(TService) 
    procedure ServiceStart(Sender: TService; var Started: Boolean); 
    procedure ServiceStop(Sender: TService; var Stopped: Boolean); 
    private 
    { Private declarations } 
    public 
     { Public declarations } 
    end; 

VAR 
    timerID : UINT; 

const 
SECONDS = 900000; 

procedure TFileCopy.ServiceStart(Sender: TService; 
    var Started: Boolean); 
Begin 
timerID := 0; //Probably not needed. 
timerID := SetTimer(0, 1, SECONDS, @MyTimerProc); 
End; 

procedure MyTimerProc(hWindow : HWND; uMsg : cardinal; idEvent : cardinal;dwTime : DWORD); stdcall; 
Begin 
    //Kill timer while trying.. If this function takes longer than the interval to run, I didn't want a build up. If that was possible. 
    KillTimer(0, timerID); 
    timerID := 0; //Is it needed? 
//DO WORK. 
{ 
//I was Connecting to a Network Drive, Minutes, seconds..... 
//I only wanted to run this Every day at 2 AM. 
//So I had my timer set to 15 minutes, once it got between 30 and 10 minutes of my 2 AM deadline, 
//i killed the existing timer and started a new one at 60000. 
//If it was within 10 minutes of my 2 AM, my interval changed to 500. 
//This seems to work for me, my files get copied everyday at 2 AM. 
} 
//Work Complete. Start timer back up. 
    timerID := SetTimer(0, 1, SECONDS, @MyTimerProc); 
End; 

procedure TFileCopy.ServiceStop(Sender: TService; 
    var Stopped: Boolean); 
Begin 
if timerID > 0 then 
    KillTimer(0, timerID); 
End; 

Natürlich hatte ich einige try..catch in den meisten Orten entlang mit dem Schreiben von Protokollen und E-Mail .... ich habe Ein Service, der diese Techniken seit über einem Jahr nutzt. Es ist keineswegs die Regel. Bitte sagen Sie mir, ob es einen besseren Weg gibt. Ich bin immer auf der Suche nach Möglichkeiten, mein Delphi-Wissen zu verbessern. Entschuldigung, wenn ich die Frist für die Veröffentlichung einer Frage verpasst habe.

-Trey Aughenbaugh

Verwandte Themen