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

Windows服务:在指定时间工作(Delphi),windows,delphi,winapi,Windows,Delphi,Winapi,只是检查在编写Windows服务时是否有任何最佳实践 服务(单线程)需要在指定的时间间隔内工作,目前我只能想到: 使用sleep(),然后检查循环中的时间 使用滴水器 有什么建议吗?我会用睡眠 这两个选项都没有精确的时间保证,但睡眠会将资源返回给其他进程。这需要服务吗?您可以在Windows中设置计划任务吗?您的服务是否为单线程并不重要,因为服务的代码总是在不同的线程上下文中调用: 服务管理器将启动、停止、暂停和恢复服务执行,并请求当前服务状态 服务本身将至少有一个线程执行实际工作,它需要对来

只是检查在编写Windows服务时是否有任何最佳实践

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

  • 使用sleep(),然后检查循环中的时间
  • 使用滴水器

  • 有什么建议吗?

    我会用睡眠


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

    这需要服务吗?您可以在Windows中设置计划任务吗?

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

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

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

    是否使用Sleep()或timer消息也取决于服务线程中消息泵的可用性。如果没有消息泵,则应使用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;
    

    也可以正常工作,然后可以使用Windows计时器消息。

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

    切勿在服务中使用TTimer,它不会始终按照您的预期运行,而且它不是线程安全的


    在服务中,我总是使用自己的时间间隔变量,并在任务执行时间之间休眠。为了保持服务的响应性,我会短时间睡眠,通常为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;
    
    当然,我在大多数地方都做了一些尝试,包括写日志和发电子邮件。。。。 我已经使用这些技术运行了一年多的服务。 这绝对不是规则。 请告诉我有没有更好的办法。 我一直在寻找提高德尔福知识的方法。 另外,如果我错过了发布问题的最后期限,我也很抱歉


    -Trey Aughenbaugh

    计时器消息将与消息循环一起使用,如果队列中没有消息,则GetMessage()会阻塞。无论哪种方式,资源都不会被浪费。这也是我想到的第一个问题。有趣的阅读。它有非常有效的观点,一些评论也是如此。我特别喜欢这句话:“使用现有的服务比使用自己的服务更好。但是……Windows任务调度器太差劲了。”所以是的,任务调度器应该是第一件要尝试的事情,编写服务应该只在极端情况下进行。但是这些情况是存在的,我只是在与任务调度器服务斗争了很长一段时间之后才编写了一个调度服务,最后不得不承认它不够通用。除非您真的需要,否则我会避免消息循环。第一个循环基本上就是我所做的,尽管是在一个线程中。
    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;