Windows服务:在指定时间工作(Delphi)

时间:2009-07-02 09:17:36

标签: windows delphi winapi

在编写Windows服务时,只需检查是否有最佳做法。

服务(单线程)需要按指定的时间间隔工作,现在我只能想到:

  1. 使用sleep(),然后检查循环中的时间?
  2. 使用TTimer?
  3. 有什么建议吗?

6 个答案:

答案 0 :(得分:13)

您的服务是单线程的并不重要,因为服务的代码总是在不同的线程上下文中调用:

  • 服务管理器将启动,停止,暂停和恢复服务执行,并请求当前的服务状态。

  • 服务本身将至少有一个执行实际工作的线程,需要对来自服务管理器的请求做出反应,根据请求更改服务执行状态,并返回所请求的信息。服务需要在合理的短时间内响应来自服务管理器的请求,否则它会认为服务被挂起并将其终止。这就是为什么 - 如果服务可能有长期执行或阻塞代码 - 拥有多个服务线程可能会更好。

是否使用 Sleep()或计时器消息还取决于服务线程中消息泵的可用性。如果您没有消息泵,则应使用 Sleep()或计时器回调。如果你有一个消息泵,因为你需要通过Windows消息与其他进程或线程通信,或者你需要做OLE的东西,那么使用定时器消息可能是最简单的。

几年前,我编写了一个服务,用于定时执行任务,类似于Windows at或Unix cron功能。它不使用大部分VCL,只使用一些基类。该服务的 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计时器消息。

答案 1 :(得分:10)

这需要是一项服务吗?你可以在Windows中设置一个计划任务吗?

答案 2 :(得分:3)

我会使用睡眠。

这两个选项都没有确切的时间保证,但是sleep会将资源返回给其他进程。

答案 3 :(得分:3)

TTimer不是线程安全的。如果您必须在线程中使用类似TTimer的方法,我建议DSiWin32使用TDSiTimer。

答案 4 :(得分:2)

永远不要在服务中使用TTimer,它不会总是按照您的预期运行,并且它不是线程安全的。

在服务中,我总是使用自己的时间间隔变量,并在任务执行时间之间休眠。为了使服务保持响应,我通常会短时间睡眠1-2000毫秒,然后在检查我的间隔之前处理消息,以了解是否是执行“任务”的时间。如果还没有时间,请回到睡眠状态,并在循环后再次检查。通过这种方式,您可以返回资源,但也可以在下一个任务执行之前响应用户输入(停止,暂停)。

答案 5 :(得分:0)

我在服务中使用类似的东西:

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;

当然,我在大多数地方都有一些Try..Catch,还有写日志和发送电子邮件.... 我已经使用这些技术运行了一年多的服务。 绝不是规则。 请告诉我是否有更好的方法。 我一直在寻找提高德尔福知识的方法。 另外,如果我错过了发布问题的截止日期,那就很抱歉。

-Trey Aughenbaugh