unit IdleWorker;
interface
uses
System.Generics.Collections, System.Generics.Defaults,
System.Messaging, System.SysUtils, System.TimeSpan, System.DateUtils;
type
TIdleWorker =
class
private const
MinWorkingTime = 20;
DefaultWorkingTime = 50;
type
TTask =
record
Action: TProc;
ExecuteAfter: TDateTime;
end;
private
FTasks: TList<TTask>;
FWorkingTime: Cardinal;
procedure SetWorkingTime(
const Value: Cardinal );
protected
procedure HandleIdleMessage(
const Sender: TObject;
const m: TMessage );
public
constructor Create( );
destructor Destroy;
override;
procedure Execute( Action: TProc );
overload;
procedure Execute( Action: TProc; ADelay: TTimeSpan );
overload;
procedure Execute( Action: TProc; ADelay: Cardinal );
overload;
procedure Execute( Action: TProc; AExecuteAfter: TDateTime );
overload;
property WorkingTime: Cardinal
read FWorkingTime
write SetWorkingTime
default DefaultWorkingTime;
private
class var _Default: TIdleWorker;
protected
class destructor Destroy;
public
class function Default: TIdleWorker;
end;
implementation
uses
System.Diagnostics,
FMX.Types;
{ TIdleWorker }
constructor TIdleWorker.Create;
begin
inherited;
FWorkingTime := DefaultWorkingTime;
FTasks := TList<TTask>.Create( TComparer<TTask>.Construct(
function(
const L, R: TTask ): integer
begin
Result := CompareDateTime( R.ExecuteAfter, L.ExecuteAfter );
end ) );
TMessageManager.DefaultManager.SubscribeToMessage( TIdleMessage, HandleIdleMessage );
end;
class function TIdleWorker.
Default: TIdleWorker;
begin
if not Assigned( _Default )
then
_Default := TIdleWorker.Create( );
Result := _Default;
end;
class destructor TIdleWorker.Destroy;
begin
FreeAndNil( _Default );
end;
destructor TIdleWorker.Destroy;
begin
TMessageManager.DefaultManager.Unsubscribe( TIdleMessage, HandleIdleMessage );
FTasks.Free;
inherited;
end;
procedure TIdleWorker.Execute( Action: TProc; ADelay: TTimeSpan );
begin
Execute( Action, Now + ADelay );
end;
procedure TIdleWorker.Execute( Action: TProc );
begin
Execute( Action, Now );
end;
procedure TIdleWorker.Execute( Action: TProc; AExecuteAfter: TDateTime );
var
LTask: TTask;
begin
LTask.Action := Action;
LTask.ExecuteAfter := AExecuteAfter;
FTasks.Add( LTask );
FTasks.Sort( );
end;
procedure TIdleWorker.HandleIdleMessage(
const Sender: TObject;
const m: TMessage );
var
LTask: TTask;
LSW: TStopwatch;
begin
LSW := TStopwatch.StartNew( );
while ( LSW.ElapsedMilliseconds < FWorkingTime )
do
begin
if ( FTasks.Count > 0 )
and ( FTasks.Last.ExecuteAfter <= Now )
then
begin
LTask := FTasks.Extract( FTasks.Last );
LTask.Action( );
end
else
Break;
end;
end;
procedure TIdleWorker.SetWorkingTime(
const Value: Cardinal );
begin
if Value >= MinWorkingTime
then
FWorkingTime := Value;
end;
procedure TIdleWorker.Execute( Action: TProc; ADelay: Cardinal );
begin
Execute( Action, IncMilliSecond( Now, ADelay ) );
end;
end.