【问题标题】:What is the correct way to implement server timer in intraweb usersession在intraweb usersession中实现服务器计时器的正确方法是什么
【发布时间】:2023-03-23 02:36:01
【问题描述】:

我有一个简单的内部网络项目,结构如下

用户会话: 有一个计时器来检查数据库并加载更改,进行一些数据格式化并将结果放入内存表中。

主窗体: 有一个 IWTimer 来检查 Usersession 中的内存表是否更改并在需要时更新其内容。

我不使用 IWTimer 直接检查数据库,因为我不希望浏览器中有一些影响用户体验的等待时间。

问题是用户会话似乎没有消息队列,正常计时器不起作用。所以,我在 Jedi VCL 中使用了 JvThreadTimer。我在 UserSession.Create 事件中创建它并在 Destroy 事件中停止并释放它。它做得很好。

但是,当我关闭服务器应用程序时,它会立即弹出 EidClosedSocket 并显示消息“已断开连接”。在 MainForm / Usersession / ServerControl Unit 中的 Destroy 事件之前触发了异常,因此我无法找到在 Timer 触发异常之前停止它的方法。

那么有没有适当的方法在 usersession 中设置服务器端计时器,或者是否有其他方法可以进行不影响浏览器使用的定期数据库检查?

提前感谢您的帮助。

【问题讨论】:

    标签: delphi timer intraweb


    【解决方案1】:

    首先。不要在服务/网络服务器等非窗口应用程序中使用 VCL 的东西和依赖于窗口句柄的东西。

    满足该要求的最简单方法是使用带有事件对象的线程,该事件对象在应用程序启动时分派。

    它应该看起来像这样:

    type
      TTimerThread = class(TThread)
      private
        fSig : TSimpleEvent;
        procedure DoTimer;
      protected
        procedure Execute; override;
      public
        procedure SignalTerminate;
    
        constructor Create;
        destructor Destroy; override;
      end;
    
    { TSessionGarbageCollector }
    
    constructor TTimerThread .Create;
    begin
         fSig := TSimpleEvent.Create;
    
         inherited Create(False);
    end;
    
    destructor TTimerThread.Destroy;
    begin
         fSig.Free;
    
         inherited;
    end;
    
    procedure TTimerThread.Execute;
    begin
         try
            // e.g. every 10 seconds
            while fSig.WaitFor(10000) = wrTimeout do
            begin
                 if Terminated then
                    break;
    
                 try
                    // #################################################
                    // #### Run the garbage collector from time to time
                    DoTimer;
                 except
                       on E : Exception do
                       begin
                            // your timer specific exception handling here
                       end;
                 end;
            end;
         except
               on F : Exception do
               begin
                    // your global exception handling here 
               end;
         end;
    end;
    
    procedure TSessionGarbageCollector.DoTimer;
    begin
         // whatever you like your timer to do
    end;
    
    procedure TSessionGarbageCollector.SignalTerminate;
    begin
         Terminate;
    
         fSig.SetEvent;
    end;
    

    另外请注意,如果您的模块是 dll(如 isapi dll),则不应在初始化子句中调度线程。寻找一个你知道 dllmain 已经被处理过的地方。

    【讨论】:

    • 感谢您的回答。我测试了您的代码,问题与使用 TJvThreadTimer 完全相同。实际上,您的代码与 TJvThreadTimer 进行了类似的工作。问题是我找不到在引发异常之前调用线程停止的地方。此外,在执行方法中也没有发生异常,因为没有触发 on 异常。
    • 什么异常?您需要显示一些代码,以便我们查看您如何使用计时器。要停止此线程,您需要调用 aTimer.SignalTerminate; aTimer.WaitFor; aTimer.Free;按那个顺序。
    • 异常已经在问题中了。带有消息“已断开”的 EidClosedSocket。我在您的代码中添加了两个属性,即间隔和 OnTimer 事件。我在用户会话的 OnCreate 事件中创建对象。它在每个间隔调用 OnTimer 事件效果很好。但是,当我终止活动会话或关闭内置调试表单时。 EidClosedSocket 异常弹出窗口。调用堆栈显示它在 InIOHandler 中。只要有一个可以在异常之前执行的地方放置它,您的终止程序就可以了。
    • 例外情况完全不同。此异常与计时器本身无关。如果在计时器例程中发生这种情况,您需要在那里处理/吞下它(不要让异常从线程中逃逸)。
    • 感谢您的建议。我知道异常没有直接发生在计时器中。但它只存在于有活动线程计时器的地方。这就是为什么我要问在这种情况下实现计时器的正确方法是什么。
    猜你喜欢
    • 1970-01-01
    • 2012-11-07
    • 1970-01-01
    • 2023-04-09
    • 2011-05-06
    • 2021-11-11
    • 1970-01-01
    • 2017-09-14
    • 1970-01-01
    相关资源
    最近更新 更多