mormot.core.threads--TBlockingProcess

海利鸟 2024-07-09 08:09:01 阅读 97

mormot.core.threads--TBlockingProcess

<code>type

/// TBlockingProcess 实例的当前状态

TBlockingEvent = (

evNone, // 无状态

evWaiting, // 等待状态

evTimeOut, // 超时状态

evRaised); // 触发状态

{$M+} // 开启内存管理消息,用于调试

/// 用于等待某个进程完成的信号量

// - 例如,在mormot.rest.server.pas中的TBlockingCallback使用

// - 一旦创建,进程将通过WaitFor调用阻塞,当后台线程调用NotifyFinished时释放

TBlockingProcess = class(TSynEvent)

protected

fTimeOutMs: integer; // 超时毫秒数

fEvent: TBlockingEvent; // 当前事件状态

fOwnedSafe: boolean; // 是否拥有同步锁

fSafe: PSynLocker; // 同步锁指针

procedure ResetInternal; virtual; // 重置关联参数(可重写)

public

/// 初始化信号量实例

// - 指定阻塞执行应视为失败的超时毫秒数(如果设置为0,则使用默认值3000)

// - 应提供一个关联的互斥锁

constructor Create(aTimeOutMs: integer; aSafe: PSynLocker);

reintroduce; overload; virtual;

/// 初始化信号量实例

// - 指定阻塞执行应视为失败的超时毫秒数(如果设置为0,则使用默认值3000)

// - 将创建并拥有一个关联的互斥锁

constructor Create(aTimeOutMs: integer); reintroduce; overload; virtual;

/// 销毁实例

destructor Destroy; override;

/// 等待NotifyFinished()被调用或触发超时

// - 返回进程的最终状态,即evRaised或evTimeOut

function WaitFor: TBlockingEvent; reintroduce; overload; virtual;

/// 等待NotifyFinished()被调用或指定超时时间后触发超时

// - 返回进程的最终状态,即evRaised或evTimeOut

function WaitFor(TimeOutMS: integer): TBlockingEvent; reintroduce; overload; virtual;

/// 应在后台进程完成时调用

// - 调用者随后将允许其WaitFor方法返回

// - 如果成功(即状态不是evRaised或evTimeout),则返回TRUE

// - 如果实例已被锁定(例如,从TBlockingProcessPool.FromCallLocked检索时),您可以设置alreadyLocked=TRUE

function NotifyFinished(alreadyLocked: boolean = false): boolean; virtual;

/// 重置内部事件状态为evNone的包装器

// - 在WaitFor/NotifyFinished过程成功后,可用于重用相同的TBlockingProcess实例

// - 如果成功(即状态不是evWaiting),则返回TRUE,并将当前状态设置为evNone,并将Call属性设置为0

// - 如果有WaitFor当前正在进行,则返回FALSE

function Reset: boolean; virtual;

/// 围绕fSafe^.Lock的包装器

procedure Lock;

/// 围绕fSafe^.Unlock的包装器

procedure Unlock;

published

/// 进程的当前状态

// - 在WaitFor过程后,使用Reset方法来重用此实例

property Event: TBlockingEvent

read fEvent;

/// 构造函数中定义的超时周期(毫秒)

property TimeOutMs: integer

read fTimeOutMS;

end;

{$M-} // 关闭内存管理消息

/// 用于标识每个TBlockingProcessPool调用的类型

// - 允许匹配给定的TBlockingProcessPoolItem信号量

TBlockingProcessPoolCall = type integer;

/// 在TBlockingProcessPool中使用的信号量

// - 该信号量具有一个Call字段来标识每次执行

TBlockingProcessPoolItem = class(TBlockingProcess)

protected

fCall: TBlockingProcessPoolCall; // 调用标识符

procedure ResetInternal; override; // 重置内部状态

published

/// 当由TBlockingProcessPool拥有时的唯一标识符

// - Reset会将此字段恢复为其默认值0

property Call: TBlockingProcessPoolCall

read fCall;

end;

/// TBlockingProcess的类引用类型(元类)

TBlockingProcessPoolItemClass = class of TBlockingProcessPoolItem;

/// 管理TBlockingProcessPoolItem实例的池

// - 每个调用将通过唯一的TBlockingProcessPoolCall值进行标识

// - 用于模拟例如从异步事件驱动DDD进程中的阻塞执行

// - 它还允许重用TEvent系统资源

TBlockingProcessPool = class(TSynPersistent)

protected

fClass: TBlockingProcessPoolItemClass; // 池项类

fPool: TSynObjectListLightLocked; // 池

fCallCounter: TBlockingProcessPoolCall; // 设置TBlockingProcessPoolItem.Call

public

/// 初始化池,对于给定的实现类

constructor Create(aClass: TBlockingProcessPoolItemClass = nil); reintroduce;

/// 销毁池

// - 还将强制所有挂起的WaitFor触发evTimeOut

destructor Destroy; override;

/// 从内部池中预订一个TBlockingProcess

// - 出错时返回nil(例如,实例正在销毁)

// - 或者返回与此调用相对应的阻塞进程实例;

// 其Call属性将标识异步回调的调用,然后在WaitFor之后,应运行Reset方法来释放池的互斥锁

function NewProcess(aTimeOutMs: integer): TBlockingProcessPoolItem; virtual;

/// 从其调用标识符检索TBlockingProcess

// - 可用于例如从异步进程的回调中

// 设置继承自TBlockingProcess的一些附加参数,

// 然后调用NotifyFinished来释放调用者的WaitFor

// - 如果leavelocked为TRUE,则返回的实例将被锁定:调用者应在使用后执行result.Unlock或NotifyFinished(true)

function FromCall(call: TBlockingProcessPoolCall;

locked: boolean = false): TBlockingProcessPoolItem; virtual;

end;

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

TBlockingProcess 例程代码

uses

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

// 假设YourSynapseUnit包含了TSynLocker和TBlockingProcess的定义

mormot.core.os,

mormot.core.thread;

var

Process: TBlockingProcess;

EventState: TBlockingEvent;

Safe: TSynLocker;

begin

try

// 创建一个同步锁

Safe := TSynLocker.Create;

try

// 创建一个TBlockingProcess实例,超时设置为5000毫秒,并传递同步锁

Process := TBlockingProcess.Create(5000, @Safe);

try

// 模拟异步操作,这里我们直接调用WaitFor来阻塞当前线程

EventState := Process.WaitFor;

case EventState of

evNone: WriteLn('Process state is evNone (should not happen)');

evWaiting: WriteLn('Process state is evWaiting (should not happen during WaitFor)');

evTimeOut: WriteLn('Process timed out');

evRaised: WriteLn('Process was notified successfully');

end;

// 在实际应用中,这里可能是另一个线程调用NotifyFinished来释放WaitFor

// 但为了演示,我们直接调用NotifyFinished(尽管这在实际应用中可能是不必要的)

if Process.NotifyFinished then

WriteLn('NotifyFinished called successfully (for demonstration purposes only)');

// 重置TBlockingProcess实例以供重用(如果需要的话)

// 在这个例子中,我们不会重用它,但展示如何调用Reset

if Process.Reset then

WriteLn('Process reset successfully');

finally

// 销毁TBlockingProcess实例

Process.Free;

end;

finally

// 销毁同步锁

Safe.Free;

end;

except

on E: Exception do

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

end;

end.

注意:在上面的示例中,NotifyFinished的调用可能是不必要的,因为在实际应用中,它通常是由另一个线程在异步操作完成时调用的。此外,Reset的调用也取决于您是否需要重用 TBlockingProcess实例。

TBlockingProcessPool 例程代码

uses

SysUtils, Classes, // 引入SysUtils和Classes单元

// 假设YourSynapseUnit包含了TBlockingProcessPool及其相关类的定义

YourSynapseUnit;

procedure SimulateAsyncCallback(Call: TBlockingProcessPoolCall);

var

Process: TBlockingProcessPoolItem;

begin

// 从池中检索与调用标识符匹配的TBlockingProcessPoolItem

Process := TBlockingProcessPool(Owner).FromCall(Call, True); // 假设Owner是TBlockingProcessPool的引用

try

// 在这里模拟异步操作的完成

// ...

// 通知等待的进程,异步操作已完成

Process.NotifyFinished(True); // 由于我们之前已经锁定了Process,所以这里传递True

finally

// 如果我们之前没有调用NotifyFinished(True),则需要在这里解锁

// 但在这个例子中,我们调用了NotifyFinished(True),所以不需要再次解锁

// Process.Unlock; // 这行是注释掉的,因为不需要在这里解锁

end;

end;

var

Pool: TBlockingProcessPool;

Process: TBlockingProcessPoolItem;

Call: TBlockingProcessPoolCall;

begin

try

// 创建一个TBlockingProcessPool实例

Pool := TBlockingProcessPool.Create(TBlockingProcessPoolItem);

try

// 从池中预订一个新的TBlockingProcessPoolItem

Process := Pool.NewProcess(5000);

if Assigned(Process) then

begin

// 获取调用标识符(在这个例子中,我们可能不需要直接使用它,但它对于回调是必需的)

Call := Process.Call;

// 在这里,您可能会启动一个异步操作,并在其回调中调用SimulateAsyncCallback

// 但为了演示,我们直接调用SimulateAsyncCallback(尽管这在实际应用中可能是不必要的)

SimulateAsyncCallback(Call);

// 注意:在实际应用中,您不会在这里立即调用SimulateAsyncCallback,

// 因为异步操作将在另一个线程或事件处理器中完成。

// 在这里,我们只是模拟了异步回调的行为。

// 由于我们直接模拟了回调,因此不需要再次调用WaitFor。

// 在实际应用中,您将在启动异步操作后调用Process.WaitFor来等待其完成。

// 重置TBlockingProcessPoolItem以供重用(如果需要的话)

// 注意:在大多数情况下,您可能不需要在池中重用项,因为池会管理它们。

// 但如果您确实需要重置它(例如,在异常情况下),您应该小心处理锁定。

// 在这个例子中,我们不会重置它,因为池会处理它。

end;

finally

// 销毁TBlockingProcessPool实例

// 注意:在销毁池时,所有挂起的WaitFor调用都将被强制超时。

Pool.Free;

end;

except

on E: Exception do

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

end;

end.

注意:在上面的 TBlockingProcessPool示例中,SimulateAsyncCallback过程模拟了异步操作的回调。然而,在实际应用中,回调将由异步操作本身(例如,在另一个线程或事件处理器中)触发,而不是由您直接调用。此外,请注意 Owner的假设,它在这个例子中并未定义,但在实际应用中,您可能需要以某种方式访问 TBlockingProcessPool的实例,以便从回调中检索 TBlockingProcessPoolItem。这通常通过传递池实例的引用或将其存储在可从回调访问的全局/静态变量中来实现。



声明

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