Служба Windows: работать в указанное время (Delphi)

просто проверьте, есть ли какая-либо лучшая практика при написании службы Windows.

служба (однопоточная) должна работать с заданными интервалами времени, прямо сейчас я могу только думать:

  1. используйте sleep (), затем проверьте время в цикле?
  2. использовать TTimer?

какие-либо советы?

6 ответов


Это должна быть услуга? Не могли бы вы настроить запланированную задачу в Windows?


на самом деле не имеет значения, что ваша служба однопоточна, поскольку у службы всегда будет свой код, вызываемый в разных контекстах потока:

  • Диспетчер служб запустит, остановит, приостановит и возобновит выполнение службы и запросит текущее состояние службы.

  • у самой службы будет хотя бы один поток, выполняющий реальную работу, который должен реагировать на запросы от диспетчера служб, изменять выполнение службы изложить запрошенную информацию и вернуть запрошенную информацию. Служба должна реагировать на запросы от диспетчера службы в разумно короткие сроки, иначе она будет считать службу повешенной и убить ее. Вот почему-если служба может иметь длительный или блокирующий код - может быть лучше иметь более одного потока службы.

стоит ли использовать Sleep () или сообщения таймера также зависят от наличия насоса сообщения в сервисные потоки. Если у вас нет насоса сообщений, вы должны использовать Sleep () или обратные вызовы таймера. Если у вас есть насос сообщений в любом случае, потому что вам нужно общаться с другими процессами или потоками через сообщения Windows, или вам нужно сделать OLE-материал, то это может быть проще всего использовать сообщения таймера.

несколько лет назад я написал сервис для временного фонового выполнения задач, похожий на Windows at или Unix cron функциональность. Это не очень жел, только некоторые базовые классы. The Run () метод сервиса выглядит так:

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;

использует Sleep () в цикле, но решение с использованием

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

будет работать так же хорошо, а затем могут использоваться сообщения таймера Windows.


Я бы использовал сон.

оба варианта не имеют точной гарантии времени, но сон дает ресурсы назад к другим процессам.


TTimer не является threadsafe. Если вам нужно использовать ttimer-подобный подход в потоке, я бы предложил TDSiTimer из DSiWin32.


никогда не используйте TTimer в службе, он не всегда будет вести себя так, как вы ожидаете, и он не является threadsafe.

в службе я всегда использую свои собственные переменные временного интервала и сплю между временами выполнения задачи. Чтобы сохранить отзывчивость службы, я сплю в течение коротких периодов, обычно 1-2000 мс, а затем обрабатываю сообщения перед проверкой моего интервала, чтобы узнать, пришло ли время выполнить "задачу". Если это еще не время, вернитесь в сон и проверьте еще раз после - в цикле. В таким образом, вы возвращаете ресурсы, но также можете отвечать на ввод пользователя (остановка, пауза) перед выполнением следующей задачи.


Я всегда использую что-то вроде этого в сервисе:

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;

конечно, я попробовал..Ловите в большинстве мест вместе с записью в журналы и по электронной почте.... У меня есть служба, работающая с использованием этих методов уже более года. Это не является правилом. Пожалуйста, скажи мне, есть ли лучший способ. Я всегда ищу способы улучшить свои знания Делфи. Кроме того, извините, если я пропустил крайний срок для публикации вопроса.

-Трэй Aughenbaugh