Служба Windows: работать в указанное время (Delphi)
просто проверьте, есть ли какая-либо лучшая практика при написании службы Windows.
служба (однопоточная) должна работать с заданными интервалами времени, прямо сейчас я могу только думать:
- используйте sleep (), затем проверьте время в цикле?
- использовать TTimer?
какие-либо советы?
6 ответов
на самом деле не имеет значения, что ваша служба однопоточна, поскольку у службы всегда будет свой код, вызываемый в разных контекстах потока:
Диспетчер служб запустит, остановит, приостановит и возобновит выполнение службы и запросит текущее состояние службы.
у самой службы будет хотя бы один поток, выполняющий реальную работу, который должен реагировать на запросы от диспетчера служб, изменять выполнение службы изложить запрошенную информацию и вернуть запрошенную информацию. Служба должна реагировать на запросы от диспетчера службы в разумно короткие сроки, иначе она будет считать службу повешенной и убить ее. Вот почему-если служба может иметь длительный или блокирующий код - может быть лучше иметь более одного потока службы.
стоит ли использовать 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