• Delphi 服务程序[6] 服务事件详述(OnStart、OnExecute )


    Delphi 服务程序[6] 服务事件详述(OnStart、OnExecute  )

    有两个位置常用于放置服务代码:在OnExecute或OnStart事件中。

    1、OneExecute:

    • 如果需要,可用代码创建一个线程。
    • 在与服务关联的线程启动时发生。
    • 如果您没有派生新线程来处理OnStart事件处理程序中的单个服务请求,则在这里实现服务。
    • 当OnExecute事件处理程序完成时,服务线程终止。
    • 大多数OnExecute事件处理程序都包含一个调用服务线程的ProcessRequests方法的循环,以便其他服务请求不会被锁定。

    2、OnStart:

    • 创建一个包含代码的线程(TThread),并在TService.OnStart启动事件。
    • OnStartup发生在服务首次启动时,在OnExecute事件之前
    • 此事件应用于初始化服务。例如,如果每个服务请求都在一个单独的线程中处理(如果处理请求需要很多时间,这是一个好方法),那么请求的线程将在OnStart事件处理程序中生成。

    3、OneExecute 示例:

    procedure TSerTest.ServiceExecute( Sender: TService);
    const
      SecBetweenRuns = 10;
    var
      Count: Integer;
    begin
      Count := 0;
      while not Terminated do
      begin
        Inc(Count);
        if Count >= SecBetweenRuns then
        begin
          Count := 0;
          //在此处输入服务代码
          //这是动作发生的地方
          SomeProcedureInAnotherUnit;  //每10秒调用一次过程  someProcedureAnotherUnit。
        end;
        Sleep(1000);
        ServiceThread.ProcessRequests(False);
      end;
    end;  

    说明:

    在本例中,每10秒调用一次过程“someProcedureAnotherUnit”。注意,我们不使用Sleep(10000)来等待10秒。如果我们这样做,我们的服务将无法快速响应从SCM(服务控制管理器)发送的命令。相反,我们一次只睡1秒,用计数器来计算自从上次调用某个过程到另一个单元已经过了多少秒。如果希望在此处执行某些初始化而不是在OnExecute事件中执行初始化,则可以使用OnStart事件,如果发现缺少某些所需的设置并且不希望启动服务,则可以将Started变量设置为False。

    这种方式使用OnExecute方法有其优点和缺点。

    • 优势:
      • 代码很简单。您不需要创建辅助线程。
      • 暂停和恢复服务将自动处理,无需额外代码。
    • 缺点:
      • someprocedureAnotherUnit只需很短的时间即可完成,最多不应超过几秒钟。
      • 如果每次运行代码只需要很短的时间就可以完成,那么OnExecute方法工作得很好。
      • 如果代码需要很长时间才能运行,则应该考虑在OnStart事件中启动辅助线程

    4、OnStart 线程方式示例

    var
        MyServiceThread: TMyServiceThread;
    
    procedure TSerTest.ServiceStart( Sender: TService; var Started: Boolean);
    begin
      { 创建一个放置服务代码的线程的实例 }
      MyServiceThread := TMyServiceThread.Create;
      { 设置杂项。线程中需要的属性(如果有) }
      //MyServiceThread.Property1 := whatever;
      // and so on
      MyServiceThread.Resume;
    end;
    
    procedure TSerTest.ServiceStop(Sender: TService; var Stopped: Boolean);
    begin
      MyServiceThread.Terminate;
    end;

    完整线程服务示例:

    unit MyServiceThreadUnit;
    
    interface
    
    uses
      Windows, Messages, SysUtils, Classes, Graphics;
    
    type
      TMyServiceThread = class(TThread)
      private
        { Private declarations }
      protected
        procedure Execute; override;
      public
        constructor Create;
      end;
    
    implementation
    
    { TMyServiceThread }
    
    constructor TMyServiceThread.Create;
    // 创建挂起的线程,以便在恢复线程之前设置属性。
    begin
      FreeOnTerminate := True;  //{线程终止时释放自己 }
      inherited Create(True);
    end;
    
    procedure TMyServiceThread.Execute;
    const
      SecBetweenRuns = 10;
    var
      Count: Integer;
    begin
      { Place thread code here }
      while not Terminated do  // loop around until we should stop
      begin
        Inc(Count);
        if Count >= SecBetweenRuns then
        begin
          Count := 0;
          //SomeProcedureInAnotherUnit;
        end;
        Sleep(1000);
      end;
    end;
    
    end. 

    使用OnStart方法启动次线程有其优点和缺点。

    • 优势:
      • 代码稍微复杂一点,您需要关于线程的知识。
      • 通过删除Count变量和所有使用Count变量的代码,只需使用较大的值调用Sleep,就可以简化Execute方法。只是不要让睡眠值太大。
      • 另一个单元的某些程序可能需要更长的时间来完成每次跑步,我们可以睡更长的时间间隔。
      • 但请注意,我们仍应定期检查Terminated属性,以便在Windows关闭或SCM要求关闭时,线程和服务可以在合理的时间内关闭。
      • 如果每次运行的代码需要较长的时间来完成,OnStart方法就可以很好地工作
    • 缺点:
      • 暂停和恢复服务不会自动处理。您需要定义OnPause和OnContinue事件,并添加代码来通知线程何时暂停并继续。
      • 请注意,尽管您可以简单地调用线程的Pause and Resume方法,但这可能是一个非常糟糕的方法。
      • 如果线程已打开文件或活动数据库连接或活动网络连接,则只要线程暂停,这些资源就会挂起。相反,您应该在线程中设置一个属性来表示线程应该停止工作,只循环调用Sleep,直到它被告知继续或关闭。
      • 在此设置中处理服务暂停和继续的简单方法是通过将服务的AllowPause属性设置为False来禁用这些选项。

    注意:

    • 首先我要坦白你,您应该将线程的FreeOnTerminate属性设置为True,以便在服务应用程序关闭时释放它自己。这是完全错误的。实际上,让线程在服务应用程序中终止本身会导致灾难。
    • 在退出TService之前,必须确保线程从TService类中终止。这是必要的,因为如果在线程终止之前让TService退出并关闭,那么线程只会在执行任何操作的过程中被终止。这当然不好。
    • 因此,必须将线程的FreeOnTerminate属性设置为False,并在TService中等待线程完成。您可以使用线程的WaitFor方法来实现这一点。

    另一个陷阱:

    • 当手动停止服务时,将调用OnStop事件(不调用OnShutdown)。
    • 当系统关闭时,调用OnShutdown事件(不调用OnStop)。
    • 因此,为了正确地清理,您必须同时实现OnStop和OnShutdown。最好只是调用一个普通的程序来进行清理。

    我们可以这样做:

    constructor TMyServiceThread.Create;
    // Create the thread Suspended so that properties can be set before resuming the thread.
    begin
      FreeOnTerminate := False;
      inherited Create(True);
    end;

    类似于:

    type
      TGrService = class(TService)
        procedure ServiceStart(Sender: TService; var Started: Boolean);
        procedure ServiceStop(Sender: TService; var Stopped: Boolean);
        procedure ServiceShutdown(Sender: TService);
      private
        { Private declarations }
        MyServiceThread: TMyServiceThread;
        procedure ServiceStopShutdown;
      public
        function GetServiceController: TServiceController; override;
        { Public declarations }
      end;
    
    procedure TGrService .ServiceStart(Sender: TService;
      var Started: Boolean);
    begin
      MyServiceThread := TMyServiceThread.Create;
      MyServiceThread.Resume;
    end;
    
    procedure TGrService .ServiceStop(Sender: TService;
      var Stopped: Boolean);
    begin
      ServiceStopShutdown;
    end;
    
    procedure TGrService .ServiceShutdown(
      Sender: TService);
    begin
      ServiceStopShutdown;
    end;
    
    procedure TGrService .ServiceStopShutdown;
    begin
      // 此处释放线程
      if Assigned(MyServiceThread) then
      begin
        // TService必须等待线程完成(并释放它),否则,当TService结束时,线程就被终止了
        MyServiceThread.Terminate;
        MyServiceThread.WaitFor;
        FreeAndNil(MyServiceThread);
      end;
    end;
    

      

      

      

    创建时间:2021.01.21  更新时间:

    博客园 滔Roy https://www.cnblogs.com/guorongtao 希望内容对你所有帮助,谢谢!
  • 相关阅读:
    JAVA设计模式---总述篇
    Java中对象创建时的内存分配
    for循环打印空心菱形的新方法
    springcloud2.X通过actuator加载配置无效问题
    golang-错误处理
    golang-字符串
    golang-方法和接口
    golang-结构体与指针
    golang-数组、切片、映射
    golang-流程控制
  • 原文地址:https://www.cnblogs.com/guorongtao/p/14306801.html
Copyright © 2020-2023  润新知