mormot.core.threads--TSynBackgroundTimer

海利鸟 2024-07-08 15:39:00 阅读 59

mormot.core.threads--TSynBackgroundTimer

<code>type

// TSynBackgroundThreadProcess 类声明(稍后定义)

TSynBackgroundThreadProcess = class;

/// 由 TSynBackgroundThreadProcess 定期执行的事件回调

TOnSynBackgroundThreadProcess = procedure(Sender: TSynBackgroundThreadProcess) of object;

/// 能够以给定周期运行方法的 TThread 类

TSynBackgroundThreadProcess = class(TSynBackgroundThreadAbstract)

protected

fOnProcess: TOnSynBackgroundThreadProcess; // 定期执行的方法回调

fOnException: TNotifyEvent; // 当 OnProcess 引发异常时执行的事件回调

fOnProcessMS: cardinal; // 定期执行任务的时间间隔(毫秒)

fStats: TSynMonitor; // 处理统计信息

procedure ExecuteLoop; override; // 重写执行循环

public

/// 初始化线程以进行周期性任务处理

// - 当 ProcessEvent.SetEvent 被调用或自上次处理以来过去了 aOnProcessMS 毫秒时,将调用 aOnProcess

// - 如果 aOnProcessMS 为 0,则等待直到 ProcessEvent.SetEvent 被调用

// - 您可以定义一些回调来嵌套线程执行,例如,分配给 TRestServer.BeginCurrentThread/EndCurrentThread

constructor Create(const aThreadName: RawUtf8;

const aOnProcess: TOnSynBackgroundThreadProcess;

aOnProcessMS: cardinal; const aOnBeforeExecute: TOnNotifyThread = nil;

const aOnAfterExecute: TOnNotifyThread = nil;

aStats: TSynMonitorClass = nil;

CreateSuspended: boolean = false); reintroduce; virtual;

/// 终结线程并等待其结束

destructor Destroy; override;

/// 访问周期性任务的实现事件

property OnProcess: TOnSynBackgroundThreadProcess

read fOnProcess;

/// 当 OnProcess 引发异常时执行的事件回调

// - 提供的 Sender 参数是引发的异常实例

property OnException: TNotifyEvent

read fOnException write fOnException;

published

/// 访问周期性任务处理的延迟时间(毫秒)

property OnProcessMS: cardinal

read fOnProcessMS write fOnProcessMS;

/// 处理统计信息

// - 如果在类构造函数中 aStats 为 nil,则可能为 nil

property Stats: TSynMonitor

read fStats;

end;

// TSynBackgroundTimer 类声明(稍后定义)

TSynBackgroundTimer = class;

/// 由 TSynBackgroundThreadProcess 定期执行的事件回调(特定于 TSynBackgroundTimer)

// - 如果此任务 FIFO 中没有挂起的消息,则 Msg 为 ''

// - 对于此任务 FIFO 中的每个挂起消息,都会设置 Msg

// - 在 mORMot 1 中,有一个 TWaitEvent 参数,现在已被移除

TOnSynBackgroundTimerProcess = procedure(Sender: TSynBackgroundTimer;

const Msg: RawUtf8) of object;

/// TSynBackgroundTimer 内部注册列表使用的记录类型

TSynBackgroundTimerTask = record

OnProcess: TOnSynBackgroundTimerProcess; // 处理任务的回调过程

Secs: cardinal; // 任务执行的间隔时间(秒)

NextTix: Int64; // 下一次执行任务的时间戳

Msg: TRawUtf8DynArray; // 与此任务关联的待处理消息队列

MsgSafe: TLightLock; // 保护 Msg[] 列表的轻量级锁

end;

/// 存储 TSynBackgroundTimer 内部注册列表的动态数组类型

TSynBackgroundTimerTaskDynArray = array of TSynBackgroundTimerTask;

/// 能够在后台线程中以定期的速度运行一个或多个任务的线程类

// - 例如,通过继承自 TRestBackgroundTimer 的 TRest.TimerEnable/TimerDisable 方法使用

// - 每个进程可以有自己的文本消息 FIFO 队列

// - 如果您期望更新某些 GUI,则应该使用 TTimer 组件(例如,周期为 200ms),

// 因为 TSynBackgroundTimer 将使用它自己的独立线程

TSynBackgroundTimer = class(TSynBackgroundThreadProcess)

protected

fTask: TSynBackgroundTimerTaskDynArray; // 内部任务列表

fTasks: TDynArrayLocked; // 任务列表的封装和同步访问

fProcessing: boolean; // 标记当前是否有任务正在处理

fProcessingCounter: integer; // 处理中的任务计数器(可能是内部使用)

procedure EverySecond(Sender: TSynBackgroundThreadProcess); // 每秒执行一次的回调,用于处理任务

function Find(const aProcess: TMethod): PtrInt; // 查找已注册的任务(内部使用)

function Add(const aOnProcess: TOnSynBackgroundTimerProcess;

const aMsg: RawUtf8; aExecuteNow: boolean): boolean; // 添加任务到内部列表

public

/// 初始化线程以进行周期性任务处理

// - 您可以定义一些回调来嵌套线程执行,例如,

// 分配给 TRestServer.BeginCurrentThread/EndCurrentThread,如 TRestBackgroundTimer.Create 所做

constructor Create(const aThreadName: RawUtf8;

const aOnBeforeExecute: TOnNotifyThread = nil;

aOnAfterExecute: TOnNotifyThread = nil;

aStats: TSynMonitorClass = nil;

aLogClass: TSynLogClass = nil); reintroduce; virtual;

/// 销毁线程

destructor Destroy; override;

/// 为在固定秒数周期上运行的任务定义一个处理方法

// - 对于 mORMot 服务上的后台进程,请考虑使用 TRest 的

// TimerEnable/TimerDisable 方法及其关联的 BackgroundTimer 线程

procedure Enable(const aOnProcess: TOnSynBackgroundTimerProcess;

aOnProcessSecs: cardinal);

/// 取消定义在固定秒数周期上运行的任务

// - aOnProcess 应通过之前的 Enable() 方法调用进行注册

// - 成功时返回 true,如果提供的任务未注册则返回 false

// - 对于 mORMot 服务上的后台进程,请考虑使用 TRestServer 的

// TimerEnable/TimerDisable 方法及其 TSynBackgroundTimer 线程

function Disable(const aOnProcess: TOnSynBackgroundTimerProcess): boolean;

/// 在任务的下一次执行期间添加要处理的消息

// - 提供的消息将被添加到与 aOnProcess 关联的内部 FIFO 列表中,

// 然后作为 aMsg 参数传递给每次调用

// - 如果 aExecuteNow 为 true,则不会等待下一个 aOnProcessSecs 的发生

// - aOnProcess 应通过之前的 Enable() 方法调用进行注册

// - 成功时返回 true,如果提供的任务未注册则返回 false

function EnQueue(const aOnProcess: TOnSynBackgroundTimerProcess;

const aMsg: RawUtf8; aExecuteNow: boolean = false): boolean; overload;

/// 在任务的下一次执行期间添加要处理的消息(格式化版本)

// - ...(与上一个 EnQueue 重载类似,但允许格式化消息)

function EnQueue(const aOnProcess: TOnSynBackgroundTimerProcess;

const aMsgFmt: RawUtf8; const Args: array of const;

aExecuteNow: boolean = false): boolean; overload;

/// 从处理列表中删除消息

// - 提供的消息将在与 aOnProcess 关联的内部 FIFO 列表中进行搜索,

// 如果找到则从列表中删除

// - aOnProcess 应通过之前的 Enable() 方法调用进行注册

// - 成功时返回 true,如果提供的消息未注册则返回 false

function DeQueue(const aOnProcess: TOnSynBackgroundTimerProcess;

const aMsg: RawUtf8): boolean;

/// 不等待下一个 aOnProcessSecs 发生,立即执行任务

// - aOnProcess 应通过之前的 Enable() 方法调用进行注册

// - 成功时返回 true,如果提供的任务未注册则返回 false

function ExecuteNow(const aOnProcess: TOnSynBackgroundTimerProcess): boolean;

/// 不等待下一个 aOnProcessSecs 发生,仅执行一次任务

// - aOnProcess 不需要通过之前的 Enable() 方法调用进行注册

function ExecuteOnce(const aOnProcess: TOnSynBackgroundTimerProcess): boolean;

/// 等待直到没有后台任务正在处理

procedure WaitUntilNotProcessing(timeoutsecs: integer = 10);

/// 对内部任务列表的低级访问

property Task: TSynBackgroundTimerTaskDynArray read fTask;

/// 对内部任务列表封装和安全的低级访问

property Tasks: TDynArrayLocked read fTasks;

/// 返回当前是否有任务正在处理

property Processing: boolean read fProcessing;

end;

以下是一个使用 TSynBackgroundTimer类的示例。在这个示例中,我们将创建一个 TSynBackgroundTimer实例,启用一个周期性任务,向该任务添加消息,并等待一段时间以观察其执行。请注意,由于 TSynBackgroundTimer是设计为在后台线程中运行的,因此我们需要确保主线程不会立即退出,以便能够看到后台任务的效果。

uses

SysUtils, // 包含WriteLn等标准输出函数

mormot.core.threads;

// 定义一个处理定时任务的回调过程

procedure MyTimerTask(Sender: TObject; const Msg: RawUtf8);

begin

WriteLn('Timer task executed at ' + DateTimeToStr(Now) + '. Message: ' + Msg);

// 在这里执行定时任务的其他逻辑...

end;

var

Timer: TSynBackgroundTimer;

begin

try

// 创建TSynBackgroundTimer实例

Timer := TSynBackgroundTimer.Create('MyBackgroundTimer');

try

// 启用一个周期性任务,每2秒执行一次

Timer.Enable(@MyTimerTask, 2);

// 向任务队列添加消息

Timer.EnQueue(@MyTimerTask, 'Hello from the background timer!');

// 假设我们想要等待一段时间来观察后台定时器的行为

// 注意:在实际应用中,您可能不需要这样做,因为主线程可能会执行其他任务

WriteLn('Waiting for 10 seconds...');

Sleep(10000); // 等待10秒

// 如果需要,可以禁用周期性任务(在这个示例中我们不会禁用它)

// Timer.Disable(@MyTimerTask);

// 注意:由于我们调用了Sleep,主线程被阻塞了,因此我们可以看到后台定时器的输出

// 在实际应用中,您可能不需要这样做,因为主线程可能会继续执行其他任务

finally

// 销毁TSynBackgroundTimer实例

// 注意:在实际应用中,您可能希望等待所有后台任务完成后再销毁定时器

// 但在这个简单示例中,我们立即销毁它

Timer.Free;

// 由于我们立即销毁了定时器,并且主线程继续执行(尽管在这个示例中被Sleep阻塞了),

// 因此后台线程可能在定时器被销毁后仍然尝试执行回调,这可能会导致访问违规。

// 在实际应用中,您应该确保在销毁定时器之前所有后台任务都已经完成。

// 一个简单的方法是调用Timer.WaitUntilNotProcessing,但这在这个示例中是不必要的,

// 因为我们立即销毁了定时器并且知道没有更多的任务会被添加。

end;

except

on E: Exception do

WriteLn('Error: ' + E.Message);

end;

// 注意:在实际应用中,主线程可能会继续执行其他任务,

// 而后台定时器将在其自己的线程中继续运行,直到被禁用或销毁。

// 在这个示例中,由于我们调用了Sleep并且立即销毁了定时器,

// 因此后台线程可能没有机会执行更多的回调。

WriteLn('Program ended.');

// 在实际应用中,您可能希望在这里添加更多的清理代码或继续执行其他任务。

end.

重要注意事项

  1. 在上述示例中,我们调用了 Sleep(10000);来模拟主线程中的其他工作,以便我们可以看到后台定时器的行为。在实际应用中,您可能不需要这样做,因为主线程可能会执行其他有用的任务。
  2. 我们立即销毁了 TSynBackgroundTimer实例,这在实际应用中可能不是最佳做法。在销毁定时器之前,您应该确保所有后台任务都已经完成。一个更安全的方法是调用 Timer.WaitUntilNotProcessing(尽管在这个简单示例中它是不必要的)。但是,请注意,如果定时器被禁用或没有更多的任务被添加,那么调用 WaitUntilNotProcessing可能会立即返回。
  3. 由于我们调用了 Sleep并且立即销毁了定时器,因此后台线程可能没有机会执行更多的回调。在实际应用中,您应该确保在销毁定时器之前给后台线程足够的时间来完成其工作。
  4. 请确保将 'YourSynapseUnit'替换为包含 TSynBackgroundTimer定义的实际单元名称。

当然,以下是根据上述类定义编写的TSynBackgroundThreadProcessTSynBackgroundTimer两个类的例程代码。请注意,由于这些类可能依赖于特定的库(如mORMot),以下示例将尽可能地保持通用性,并假设您已经有一个适当的环境来运行这些代码。

TSynBackgroundThreadProcess 例程代码

uses

SysUtils, Classes, // 引入SysUtils和Classes单元以使用WriteLn和TThread等

// 假设YourSynapseUnit包含了TSynBackgroundThreadProcess的定义

YourSynapseUnit;

procedure MyProcessMethod(Sender: TSynBackgroundThreadProcess);

begin

WriteLn('Process method called in background thread.');

// 在这里执行您的后台处理逻辑

end;

var

BGThread: TSynBackgroundThreadProcess;

begin

try

// 创建TSynBackgroundThreadProcess实例

BGThread := TSynBackgroundThreadProcess.Create(

'MyBackgroundThread', // 线程名称

MyProcessMethod, // 周期性执行的方法

1000, // 周期时间,单位为毫秒

nil, // OnBeforeExecute回调,这里不使用

nil // OnAfterExecute回调,这里不使用

// aStats和其他参数根据需要进行设置

);

try

// 启动线程(注意:在TSynBackgroundThreadProcess的构造函数中,

// 如果CreateSuspended参数为false,则线程将自动启动)

// 在这个例子中,我们假设CreateSuspended默认为false

// 等待一段时间以观察后台线程的行为

// 注意:在实际应用中,您可能不需要这样做,因为主线程可能会继续执行其他任务

Sleep(5000); // 等待5秒

finally

// 销毁线程对象(注意:在析构函数中,线程将尝试优雅地终止)

BGThread.Free;

// 等待线程真正结束(可选,但在这个例子中我们依赖析构函数的行为)

end;

except

on E: Exception do

WriteLn('Error: ' + E.Message);

end;

WriteLn('Program ended.');

end.

注意:在上面的示例中,我假设TSynBackgroundThreadProcess的构造函数有一个CreateSuspended参数(这在标准的TThread构造函数中是存在的),但根据您提供的类定义,这个参数实际上并没有在TSynBackgroundThreadProcess的构造函数中明确列出。如果TSynBackgroundThreadProcess是自动启动线程的,那么您可能不需要显式调用任何启动方法。

TSynBackgroundTimer 例程代码

uses

SysUtils, // 引入SysUtils单元以使用WriteLn

// 假设YourSynapseUnit包含了TSynBackgroundTimer的定义

YourSynapseUnit;

procedure MyTimerProcess(Sender: TSynBackgroundTimer; const Msg: RawUtf8);

begin

WriteLn('Timer process called in background thread. Message: ' + Msg);

// 在这里执行您的定时任务逻辑

end;

var

Timer: TSynBackgroundTimer;

begin

try

// 创建TSynBackgroundTimer实例

Timer := TSynBackgroundTimer.Create(

'MyBackgroundTimer' // 线程名称

// 其他参数根据需要进行设置,这里省略了OnBeforeExecute、OnAfterExecute、aStats和aLogClass

);

try

// 启用一个周期性任务,每2秒执行一次

Timer.Enable(@MyTimerProcess, 2);

// 向任务队列添加消息,并请求立即执行(尽管在这个上下文中,立即执行可能不会立即发生)

Timer.EnQueue(@MyTimerProcess, 'Hello from background timer!', true);

// 等待一段时间以观察后台定时器的行为

// 注意:在实际应用中,您可能不需要这样做,因为主线程可能会继续执行其他任务

Sleep(10000); // 等待10秒

// 禁用周期性任务(在这个示例中我们不会禁用它,但展示了如何禁用)

// Timer.Disable(@MyTimerProcess);

finally

// 销毁TSynBackgroundTimer实例(注意:在实际应用中,您可能希望等待所有后台任务完成后再销毁定时器)

// 但在这个简单示例中,我们立即销毁它

Timer.Free;

// 由于我们立即销毁了定时器,并且主线程继续执行(尽管在这个示例中被Sleep阻塞了),

// 因此后台线程可能没有机会执行更多的回调。

// 在实际应用中,您应该确保在销毁定时器之前给后台线程足够的时间来完成其工作。

end;

except

on E: Exception do

WriteLn('Error: ' + E.Message);

end;

WriteLn('Program ended.');

end.

在上面的TSynBackgroundTimer示例中,我展示了如何创建定时器、启用周期性任务、向任务队列添加消息,并等待一段时间以观察定时器的行为。请注意,由于我们调用了Sleep并且立即销毁了定时器,因此后台线程可能没有机会执行更多的回调。在实际应用中,您应该确保在销毁定时器之前给后台线程足够的时间来完成其工作,或者调用Timer.WaitUntilNotProcessing(如果该类提供了这样的方法)来等待所有后台任务完成。然而,根据提供的类定义,TSynBackgroundTimer并没有直接提供WaitUntilNotProcessing方法,所以您可能需要实现自己的同步机制来达到这个目的。



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。