目录
unit System.Threading; //Delphi的内部用户态线程库
当Application空闲Idle时你要在UI线程勤做的事情:
扫描二维码关注公众号,回复:
13141293 查看本文章

Delphi线程内部
unit System.Classes;
function ThreadProc(const Thread: TThread): Integer;
var
FreeThread: Boolean;
{$IFDEF MACOS}
pool: Pointer;
{$ENDIF MACOS}
begin
{$IFDEF AUTOREFCOUNT}
Thread.__ObjAddRef; // this ensures the instance remains for as long as the thread is running
{$ENDIF}
TThread.FCurrentThread := Thread;
{$IF Defined(POSIX)}
if Thread.FSuspended then
pthread_mutex_lock(Thread.FCreateSuspendedMutex);
{$ENDIF POSIX}
{$IFDEF MACOS}
// Register the auto release pool
pool := objc_msgSend(objc_msgSend(objc_getClass('NSAutoreleasePool'),
sel_getUid('alloc')), sel_getUid('init'));
{$ENDIF MACOS}
try
Thread.FStarted := True;
if not Thread.Terminated then
try
Thread.Execute;
except
Thread.FFatalException := AcquireExceptionObject;
end;
finally
Result := Thread.FReturnValue;
FreeThread := Thread.FFreeOnTerminate;
Thread.DoTerminate;
Thread.FFinished := True;
SignalSyncEvent;
if FreeThread then
begin
Thread.DisposeOf;
{$IFDEF AUTOREFCOUNT}
Thread.__ObjRelease; // This will clear the thread reference that was added by setting FreeOnTerminate.
{$ENDIF}
end;
{$IFDEF AUTOREFCOUNT}
Thread.__ObjRelease; // This will clear the thread reference we added above. This may initiate disposal.
{$ENDIF}
{$IFDEF USE_LIBICU}
// Destroy Collator Cache
if IsICUAvailable then
ClearCollatorCache;
{$ENDIF}
{$IF Defined(MSWINDOWS)}
EndThread(Result);
{$ELSEIF Defined(POSIX)}
{$IFDEF MACOS}
// Last thing to do in thread is to drain the pool
objc_msgSend(pool, sel_getUid('drain'));
{$ENDIF MACOS}
{$IFDEF ANDROID}
// Detach the NativeActivity virtual machine to ensure the proper relase of JNI context attached to the current thread
PJavaVM(System.JavaMachine)^.DetachCurrentThread(PJavaVM(System.JavaMachine));
{$ENDIF ANDROID}
// Directly call pthread_exit since EndThread will detach the thread causing
// the pthread_join in TThread.WaitFor to fail. Also, make sure the EndThreadProc
// is called just like EndThread would do. EndThreadProc should not return
// and call pthread_exit itself.
if Assigned(EndThreadProc) then
EndThreadProc(Result);
pthread_exit(Result);
{$ENDIF POSIX}
end;
end;
unit System;
{ Thread support }
type
TThreadFunc = function(Parameter: Pointer): Integer;
{$IFDEF POSIX}
{$IFDEF LINUX}
type
TSize_T = Cardinal;
TSchedParam = record
sched_priority: Integer;
end;
{$DEFINE _PTHREAD_ATTR_T_DEFINED}
pthread_attr_t = record
__detachstate,
__schedpolicy: Integer;
__schedparam: TSchedParam;
__inheritsched,
__scope: Integer;
__guardsize: TSize_T;
__stackaddr_set: Integer;
__stackaddr: Pointer;
__stacksize: TSize_T;
end;
{$EXTERNALSYM pthread_attr_t}
{$ENDIF LINUX}
{$IFDEF MACOS}
const
PTHREAD_ATTR_SIZE = 36;
SCHED_PARAM_SIZE = 4;
type
TSchedParam = record
sched_priority: Integer;
opaque: array [0..SCHED_PARAM_SIZE-1] of Byte;
end;
{$DEFINE _PTHREAD_ATTR_T_DEFINED}
pthread_attr_t = record
__sig: LongInt;
opaque: array [0..PTHREAD_ATTR_SIZE-1] of Byte;
end;
{$EXTERNALSYM pthread_attr_t} // Defined in signal.h
{$ENDIF MACOS}
{$IFDEF ANDROID}
type
TSize_T = Cardinal;
{$DEFINE _PTHREAD_ATTR_T_DEFINED}
pthread_attr_t = record
flags: UInt32;
stack_base: Pointer;
stack_size: TSize_T;
guard_size: TSize_T;
sched_policy: Int32;
sched_priority: Int32;
end;
{$EXTERNALSYM pthread_attr_t}
{$ENDIF ANDROID}
type
TThreadAttr = pthread_attr_t;
PThreadAttr = ^TThreadAttr;
TBeginThreadProc = function (Attribute: PThreadAttr;
ThreadFunc: TThreadFunc; Parameter: Pointer;
var ThreadId: TThreadID): Integer;
TEndThreadProc = procedure(ExitCode: Integer);
var
BeginThreadProc: TBeginThreadProc = nil;
EndThreadProc: TEndThreadProc = nil;
{$ENDIF POSIX}
{$IFDEF MSWINDOWS}
type
TSystemThreadFuncProc = function(ThreadFunc: TThreadFunc; Parameter: Pointer): Pointer;
TSystemThreadEndProc = procedure(ExitCode: Integer);
{$NODEFINE TSystemThreadFuncProc}
{$NODEFINE TSystemThreadEndProc}
(*$HPPEMIT 'namespace System' *)
(*$HPPEMIT '{' *)
(*$HPPEMIT ' typedef void * (__fastcall * TSystemThreadFuncProc)(void *, void * );' *)
(*$HPPEMIT ' typedef void (__fastcall * TSystemThreadEndProc)(int);' *)
(*$HPPEMIT '}' *)
var
// SystemThreadFuncProc and SystemThreadEndProc are set during the startup
// code by the C++ RTL when running in a C++Builder VCL application.
SystemThreadFuncProc: TSystemThreadFuncProc = nil;
SystemThreadEndProc: TSystemThreadEndProc = nil;
function BeginThread(SecurityAttributes: Pointer; StackSize: LongWord;
ThreadFunc: TThreadFunc; Parameter: Pointer; CreationFlags: LongWord;
var ThreadId: TThreadID): THandle;
{$ENDIF}
{$IFDEF POSIX}
function BeginThread(Attribute: PThreadAttr; ThreadFunc: TThreadFunc;
Parameter: Pointer; var ThreadId: TThreadID): Integer;
{$ENDIF}
procedure EndThread(ExitCode: Integer);
{ Standard procedures and functions }
const
{ File mode magic numbers }
fmClosed = $D7B0;
fmInput = $D7B1;
fmOutput = $D7B2;
fmInOut = $D7B3;
{ Text file flags }
tfCRLF = $1; // Dos compatibility flag, for CR+LF line breaks and EOF checks
type
{ Typed-file and untyped-file record }
TFileRec = packed record (* must match the size the compiler generates: 592 bytes (616 bytes for x64) *)
Handle: NativeInt;
Mode: Word;
Flags: Word;
case Byte of
0: (RecSize: Cardinal); // files of record
1: (BufSize: Cardinal; // text files
BufPos: Cardinal;
BufEnd: Cardinal;
BufPtr: _PAnsiChr;
OpenFunc: Pointer;
InOutFunc: Pointer;
FlushFunc: Pointer;
CloseFunc: Pointer;
UserData: array[1..32] of Byte;
Name: array[0..259] of WideChar;
);
end;
{ Text file record structure used for Text files }
PTextBuf = ^TTextBuf;
TTextBuf = array[0..127] of _AnsiChr;
TTextRec = packed record (* must match the size the compiler generates: 730 bytes (754 bytes for x64) *)
Handle: NativeInt; (* must overlay with TFileRec *)
Mode: Word;
Flags: Word;
BufSize: Cardinal;
BufPos: Cardinal;
BufEnd: Cardinal;
BufPtr: _PAnsiChr;
OpenFunc: Pointer;
InOutFunc: Pointer;
FlushFunc: Pointer;
CloseFunc: Pointer;
UserData: array[1..32] of Byte;
Name: array[0..259] of WideChar;
Buffer: TTextBuf;
CodePage: Word;
MBCSLength: ShortInt;
MBCSBufPos: Byte;
case Integer of
0: (MBCSBuffer: array[0..5] of _AnsiChr);
1: (UTF16Buffer: array[0..2] of WideChar);
end;
TTextIOFunc = function (var F: TTextRec): Integer;
TFileIOFunc = function (var F: TFileRec): Integer;
procedure SetLineBreakStyle(var T: Text; Style: TTextLineBreakStyle);
function GetTextCodePage(const T: Text): Word;
procedure SetTextCodePage(var T: Text; CodePage: Word);
procedure __IOTest;
procedure SetInOutRes(NewValue: Integer);
procedure ChDir(const S: string); overload;
procedure ChDir(P: PChar); overload;
function Flush(var t: Text): Integer;
procedure _UGetDir(D: Byte; var S: UnicodeString);
procedure _LGetDir(D: Byte; var S: _AnsiStr);
procedure _WGetDir(D: Byte; var S: _WideStr);
procedure _SGetDir(D: Byte; var S: _ShortStr);
function IOResult: Integer;
procedure MkDir(const S: string); overload;
procedure MkDir(P: PChar); overload;
procedure Move(const Source; var Dest; Count: NativeInt);
procedure MoveChars(const Source; var Dest; Length: Integer); inline;
function ParamCount: Integer;
function ParamStr(Index: Integer): string;
procedure RmDir(const S: string); overload;
procedure RmDir(P: PChar); overload;
function UpCase(Ch: _AnsiChr): _AnsiChr; overload; inline;
function UpCase(Ch: WideChar): WideChar; overload; inline;
unit System.Threading; //Delphi的内部用户态线程库
{ TThreadPool.TBaseWorkerThread }
constructor TThreadPool.TBaseWorkerThread.Create(AThreadPool: TThreadPool);
begin
inherited Create(False);
// Priority := tpHigher;
FRunningEvent := TLightweightEvent.Create(False);
FThreadPool := AThreadPool;
ThreadPool.FThreads.Add(Self);
end;
destructor TThreadPool.TBaseWorkerThread.Destroy;
begin
if FRunningEvent <> nil then
FRunningEvent.WaitFor(INFINITE); // This waits for the Execute to actually be called.
if ThreadPool <> nil then
ThreadPool.FThreads.Remove(Self);
FRunningEvent.Free;
inherited Destroy;
end;
procedure TThreadPool.TBaseWorkerThread.SafeTerminate;
begin
if ThreadPool <> nil then
ThreadPool.FThreads.Remove(Self);
FreeOnTerminate := True;
Terminate;
end;
procedure TThreadPool.TBaseWorkerThread.Execute;
begin
NameThreadForDebugging(Format('Worker Thread - %s #%d ThreadPool - %p', [ClassName, TInterlocked.Increment(WorkerThreadID), Pointer(ThreadPool)]));
FRunningEvent.SetEvent;
end;
如何池化线程并进行管理:
3.1、任务及其接口ITask--->TTask
TTask = class(TAbstractTask, TThreadPool.IThreadPoolWorkItem, ITask, TAbstractTask.IInternalTask)
//............
public
class function CurrentTask: ITask; static; inline;
constructor Create; overload; // do not call this constructor!!
destructor Destroy; override;
class function Create(Sender: TObject; Event: TNotifyEvent): ITask; overload; static; inline;
class function Create(const Proc: TProc): ITask; overload; static; inline;
class function Create(Sender: TObject; Event: TNotifyEvent; const APool: TThreadPool): ITask; overload; static; inline;
class function Create(const Proc: TProc; APool: TThreadPool): ITask; overload; static; inline;
class function Future<T>(Sender: TObject; Event: TFunctionEvent<T>): IFuture<T>; overload; static; inline;
class function Future<T>(Sender: TObject; Event: TFunctionEvent<T>; APool: TThreadPool): IFuture<T>; overload; static; inline;
class function Future<T>(const Func: TFunc<T>): IFuture<T>; overload; static; inline;
class function Future<T>(const Func: TFunc<T>; APool: TThreadPool): IFuture<T>; overload; static; inline;
class function Run(Sender: TObject; Event: TNotifyEvent): ITask; overload; static; inline;
class function Run(Sender: TObject; Event: TNotifyEvent; APool: TThreadPool): ITask; overload; static; inline;
class function Run(const Func: TProc): ITask; overload; static; inline;
class function Run(const Func: TProc; APool: TThreadPool): ITask; overload; static; inline;
class function WaitForAll(const Tasks: array of ITask): Boolean; overload; static;
class function WaitForAll(const Tasks: array of ITask; Timeout: Cardinal): Boolean; overload; static;
class function WaitForAll(const Tasks: array of ITask; const Timeout: TTimeSpan): Boolean; overload; static;
class function WaitForAny(const Tasks: array of ITask): Integer; overload; static;
class function WaitForAny(const Tasks: array of ITask; Timeout: Cardinal): Integer; overload; static;
class function WaitForAny(const Tasks: array of ITask; const Timeout: TTimeSpan): Integer; overload; static;
end;
ITask = interface(TThreadPool.IThreadPoolWorkItem)
/// <summary>Waits for the task to complete execution within a specific milliseconds.</summary>
function Wait(Timeout: Cardinal = INFINITE): Boolean; overload;
/// <summary>Waits for the task to complete execution within a specific time interval.</summary>
function Wait(const Timeout: TTimeSpan): Boolean; overload;
/// <summary>Cancel the task.</summary>
procedure Cancel;
/// <summary>Checks the task's cancel staus. If the task is canceld, raise EOperationCancelled exception.</summary>
procedure CheckCanceled;
/// <summary>Starts the task, adding it to the currect queue.</summary>
function Start: ITask;
/// <summary>Return the task's status code.</summary>
function GetStatus: TTaskStatus;
/// <summary>Returns the unique integer Id</summary>
function GetId: Integer;
/// <summary>Task ID. Each Task has unique ID.</summary>
property Id: Integer read GetId;
/// <summary>Task's status code</summary>
property Status: TTaskStatus read GetStatus;
end;
3.2、未来:也是池化的任务接口
//...未来:也是池化的任务接口
IFuture<T> = interface(ITask)
[HPPGEN('HIDESBASE virtual System::DelphiInterface<IFuture__1<T> > __fastcall StartFuture() = 0')]
function Start: IFuture<T>;
function GetValue: T;
property Value: T read GetValue;
end;
/// <summary>
/// Use this record to grab a "snapshot" of a threadpool's internal state. This is useful in peering into the
/// inner workings of a threadpool for diagnostic purposes. Because the accessed threadpool continues to run,
/// This snapshot may not be strictly accurate. Do not depend on this structure for anything other than simply
/// hinting at the internal state.
/// 使用此记录可以获取线程池内部状态的“快照”。这在窥视线程池的内部工作以进行诊断时非常有用。由
/// 于被访问的线程池继续运行,因此此快照可能不完全准确。除了简单地暗示内部状态之外,不要依赖于这个结构
/// </summary>
TThreadPoolStats = record
private
//...
public
/// <summary>Total number of worker threads within the thread pool</summary>
property WorkerThreadCount: Integer read FWorkerThreadCount;
/// <summary>Corresponds to the MinWorkerThreads property</summary>
property MinLimitWorkerThreadCount: Integer read FMinLimitWorkerThreadCount;
/// <summary>Corresponds to the MaxWorkerThreads property</summary>
property MaxLimitWorkerThreadCount: Integer read FMaxLimitWorkerThreadCount;
/// <summary>Number of threads waiting for work to do</summary>
property IdleWorkerThreadCount: Integer read FIdleWorkerThreadCount;
/// <summary>Number of global queued work requests</summary>
property QueuedRequestCount: Integer read FQueuedRequestCount;
/// <summary>Number of worker threads in the process of being retired</summary>
property RetiredWorkerThreadCount: Integer read FRetiredWorkerThreadCount;
/// <summary>Running average of CPU usage</summary>
property AverageCPUUsage: Integer read FAverageCPUUsage;
/// <summary>Current snapshot of CPU usage</summary>
property CurrentCPUUsage: Integer read FCurrentCPUUsage;
/// <summary>Shared value among worker threads to manage orderly thread suspension</summary>
property ThreadSuspended: Integer read FThreadSuspended;
/// <summary>Shared value among worker threads to manage orderly thread suspension</summary>
property LastSuspendTick: Cardinal read FLastSuspendTick;
/// <summary>Used by the monitor thread to keep from creating threads too quickly</summary>
property LastThreadCreationTick: Cardinal read FLastThreadCreationTick;
/// <summary>Used by the monitor thread to only create new threads when there is work to do</summary>
property LastQueuedRequestCount: Integer read FLastQueuedRequestCount;
/// <summary>
/// When called from within a threadpool thread, returns the stats for the owning threadpool. Outside of a
/// threadpool thread, this will return the stats for the default threadpool
/// </summary>
class property Current: TThreadPoolStats read GetCurrentThreadPoolStats;
/// <summary>Returns the stats for the default threadpool</summary>
class property Default: TThreadPoolStats read GetDefaultThreadPoolStats;
end;
[HPPGen(HPPGENAttribute.mkNoDefine)]
TFuture<T> = class sealed(TTask, IFuture<T>)
private
//......
protected
function Start: IFuture<T>;
function GetValue: T;
constructor Create(Sender: TObject; Event: TFunctionEvent<T>; const Func: TFunc<T>; APool: TThreadPool); overload;
end;
3.3、并行库,也是池化的:
TParallel = class sealed
public type
TLoopState = class
private type
//.........
private
//.........
strict protected
//.........
public
procedure Break;
procedure Stop;
function ShouldExit: Boolean;
property Faulted: Boolean read GetFaulted;
property Stopped: Boolean read GetStopped;
property LowestBreakIteration: Variant read GetLowestBreakIteration;
end;
TLoopResult = record
private
//.........
public
property Completed: Boolean read FCompleted;
property LowestBreakIteration: Variant read FLowestBreakIteration;
end;
TIteratorEvent = procedure (Sender: TObject; AIndex: Integer) of object;
TIteratorStateEvent = procedure (Sender: TObject; AIndex: Integer; const LoopState: TLoopState) of object;
TIteratorEvent64 = procedure (Sender: TObject; AIndex: Int64) of object;
TIteratorStateEvent64 = procedure (Sender: TObject; AIndex: Int64; const LoopState: TLoopState) of object;
private type
//.........
public
class function &For(Sender: TObject; ALowInclusive, AHighInclusive: Integer; AIteratorEvent: TIteratorEvent): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; ALowInclusive, AHighInclusive: Integer; AIteratorEvent: TIteratorEvent; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; ALowInclusive, AHighInclusive: Integer; AIteratorEvent: TIteratorStateEvent): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; ALowInclusive, AHighInclusive: Integer; AIteratorEvent: TIteratorStateEvent; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; AStride, ALowInclusive, AHighInclusive: Integer; AIteratorEvent: TIteratorEvent): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; AStride, ALowInclusive, AHighInclusive: Integer; AIteratorEvent: TIteratorEvent; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; AStride, ALowInclusive, AHighInclusive: Integer; AIteratorEvent: TIteratorStateEvent): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; AStride, ALowInclusive, AHighInclusive: Integer; AIteratorEvent: TIteratorStateEvent; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(ALowInclusive, AHighInclusive: Integer; const AIteratorEvent: TProc<Integer>): TLoopResult; overload; static; inline;
class function &For(ALowInclusive, AHighInclusive: Integer; const AIteratorEvent: TProc<Integer>; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(ALowInclusive, AHighInclusive: Integer; const AIteratorEvent: TProc<Integer, TLoopState>): TLoopResult; overload; static; inline;
class function &For(ALowInclusive, AHighInclusive: Integer; const AIteratorEvent: TProc<Integer, TLoopState>; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(AStride, ALowInclusive, AHighInclusive: Integer; const AIteratorEvent: TProc<Integer>): TLoopResult; overload; static; inline;
class function &For(AStride, ALowInclusive, AHighInclusive: Integer; const AIteratorEvent: TProc<Integer>; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(AStride, ALowInclusive, AHighInclusive: Integer; const AIteratorEvent: TProc<Integer, TLoopState>): TLoopResult; overload; static; inline;
class function &For(AStride, ALowInclusive, AHighInclusive: Integer; const AIteratorEvent: TProc<Integer, TLoopState>; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; ALowInclusive, AHighInclusive: Int64; AIteratorEvent: TIteratorEvent64): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; ALowInclusive, AHighInclusive: Int64; AIteratorEvent: TIteratorEvent64; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; ALowInclusive, AHighInclusive: Int64; AIteratorEvent: TIteratorStateEvent64): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; ALowInclusive, AHighInclusive: Int64; AIteratorEvent: TIteratorStateEvent64; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; AStride, ALowInclusive, AHighInclusive: Int64; AIteratorEvent: TIteratorEvent64): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; AStride, ALowInclusive, AHighInclusive: Int64; AIteratorEvent: TIteratorEvent64; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; AStride, ALowInclusive, AHighInclusive: Int64; AIteratorEvent: TIteratorStateEvent64): TLoopResult; overload; static; inline;
class function &For(Sender: TObject; AStride, ALowInclusive, AHighInclusive: Int64; AIteratorEvent: TIteratorStateEvent64; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(ALowInclusive, AHighInclusive: Int64; const AIteratorEvent: TProc<Int64>): TLoopResult; overload; static; inline;
class function &For(ALowInclusive, AHighInclusive: Int64; const AIteratorEvent: TProc<Int64>; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(ALowInclusive, AHighInclusive: Int64; const AIteratorEvent: TProc<Int64, TLoopState>): TLoopResult; overload; static; inline;
class function &For(ALowInclusive, AHighInclusive: Int64; const AIteratorEvent: TProc<Int64, TLoopState>; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(AStride, ALowInclusive, AHighInclusive: Int64; const AIteratorEvent: TProc<Int64>): TLoopResult; overload; static; inline;
class function &For(AStride, ALowInclusive, AHighInclusive: Int64; const AIteratorEvent: TProc<Int64>; APool: TThreadPool): TLoopResult; overload; static; inline;
class function &For(AStride, ALowInclusive, AHighInclusive: Int64; const AIteratorEvent: TProc<Int64, TLoopState>): TLoopResult; overload; static; inline;
class function &For(AStride, ALowInclusive, AHighInclusive: Int64; const AIteratorEvent: TProc<Int64, TLoopState>; APool: TThreadPool): TLoopResult; overload; static; inline;
class function Join(Sender: TObject; AEvents: array of TNotifyEvent): ITask; overload; static;
class function Join(Sender: TObject; AEvents: array of TNotifyEvent; APool: TThreadPool): ITask; overload; static;
class function Join(Sender: TObject; AEvent1, AEvent2: TNotifyEvent): ITask; overload; static; inline;
class function Join(Sender: TObject; AEvent1, AEvent2: TNotifyEvent; APool: TThreadPool): ITask; overload; static;
class function Join(const AProcs: array of TProc): ITask; overload; static;
class function Join(const AProcs: array of TProc; APool: TThreadPool): ITask; overload; static;
class function Join(const AProc1, AProc2: TProc): ITask; overload; static; inline;
class function Join(const AProc1, AProc2: TProc; APool: TThreadPool): ITask; overload; static;
end;
当Application空闲Idle时你要在UI线程勤做的事情:
function CheckSynchronize(Timeout: Integer = 0): Boolean; //unit System.Classes;