System.TMonitor
Delphi
TMonitor = record
strict private
type
PWaitingThread = ^TWaitingThread;
TWaitingThread = record
Next: PWaitingThread;
Thread: TThreadID;
WaitEvent: Pointer;
end;
{ TSpinWait implements an exponential backoff algorithm for TSpinLock. The algorithm is as follows:
If the CPUCount > 1, then the first 10 (YieldThreshold) spin cycles (calls to SpinCycle) will use a base 2
exponentially increasing spin count starting at 4. After 10 cycles, then the behavior reverts to the same
behavior as when CPUCount = 1.
If the CPUCount = 1, then it will sleep 1ms every modulus 20 cycles and sleep 0ms every modulus 5 cycles.
All other cycles simply yield (SwitchToThread - Windows, sched_yield - POSIX). }
TSpinWait = record
private const
YieldThreshold = 10;
Sleep1Threshold = 20;
Sleep0Threshold = 5;
private
FCount: Integer;
public
procedure Reset; inline;
procedure SpinCycle;
end;
{ TSpinLock implements a very simple non-reentrant lock. This lock does not block the calling thread using a
synchronization object. Instead it opts to burn a few extra CPU cycles using the above TSpinWait type. This
is typically far faster than fully blocking since the length of time the lock is held is relatively few
cycles and the thread switching overhead will usually far outpace the few cycles burned by simply spin
waiting. }
TSpinLock = record
private
FLock: Integer;
public
procedure Enter;
procedure Exit;
end;
var
[Volatile] FLockCount: Integer;
FRecursionCount: Integer;
FOwningThread: TThreadID;
FLockEvent: Pointer;
FSpinCount: Integer;
FWaitQueue: PWaitingThread;
FQueueLock: TSpinLock;
class var CacheLineSize: Integer;
class var FDefaultSpinCount: Integer;
class procedure Spin(Iterations: Integer); static;
class function GetCacheLineSize: Integer; static;
procedure QueueWaiter(var WaitingThread: TWaitingThread);
procedure RemoveWaiter(var WaitingThread: TWaitingThread);
function DequeueWaiter: PWaitingThread;
function GetEvent: Pointer;
function CheckOwningThread: TThreadID;
class procedure CheckMonitorSupport; static; inline;
private
class function Create: PMonitor; static;
class procedure Destroy(const AObject: TObject); overload; static;
procedure Destroy; overload;
strict private
class function GetFieldAddress(const AObject: TObject): PPMonitor; inline; static;
class function GetMonitor(const AObject: TObject): PMonitor; static;
class procedure SetDefaultSpinCount(AValue: Integer); static;
function TryEnter: Boolean; overload;
function Wait(ALock: PMonitor; Timeout: Cardinal): Boolean; overload;
procedure Pulse; overload;
procedure PulseAll; overload;
private
function Enter(Timeout: Cardinal): Boolean; overload;
procedure Exit; overload;
public
{ In multi-core/multi-processor systems, it is sometimes desirable to spin for a few cycles instead of blocking
the current thread when attempting to Enter the monitor. Use SetSpinCount to set a reasonable number of times to
spin before fully blocking the thread. This value usually obtained through empirical study of the particular
situation. }
class procedure SetSpinCount(const AObject: TObject; ASpinCount: Integer); static;
{ Enter locks the monitor object with an optional timeout (in ms) value. Enter without a timeout will wait until
the lock is obtained. If the procedure returns it can be assumed that the lock was acquired. Enter with a
timeout will return a boolean status indicating whether or not the lock was obtained (True) or the attempt timed
out prior to acquire the lock (False). Calling Enter with an INFINITE timeout is the same as calling Enter
without a timeout.
TryEnter will simply attempt to obtain the lock and return immediately whether or not the lock was acuired.
Enter with a 0ms timeout is functionally equivalent to TryEnter.
Exit will potentially release the lock acquired by a call to Enter or TryEnter. Since Enter/TryEnter are
rentrant, you must balance each of those calls with a corresponding call to Exit. Only the last call to Exit will
release the lock and allow other threads to obtain it. Runtime error, reMonitorNotLocked, is generated if Exit is
called and the calling thread does not own the lock. }
class procedure Enter(const AObject: TObject); overload; static; inline;
class function Enter(const AObject: TObject; Timeout: Cardinal): Boolean; overload; static;
class procedure Exit(const AObject: TObject); overload; static;
class function TryEnter(const AObject: TObject): Boolean; overload; static;
{ Wait will atomically fully release the lock (regardless of the recursion count) and block the calling thread
until another thread calls Pulse or PulseAll. The first overloaded Wait function will assume the locked object
and wait object are the same and thus the calling thread must own the lock. The second Wait allows the given
monitor to atomically unlock the separate monitor lock object and block with the calling thread on the first
given wait object. Wait will not return (even if it times out) until the monitor lock can be acquired again. It
is possible for wait to return False (the timeout expired) after a much longer period of time has elapsed if
the locking object was being held by another thread for an extended period. When Wait returns the recursion
level of the lock has been restored.
Pulse must be called on the exact same instance passed to Wait in order to properly release one waiting thread.
PulseAll works the same as Pulse except that it will release all currently waiting threads.
Wait/Pulse/PulseAll are the same as a traditional condition variable.
}
class function Wait(const AObject: TObject; Timeout: Cardinal): Boolean; overload; static;
class function Wait(const AObject, ALock: TObject; Timeout: Cardinal): Boolean; overload; static;
class procedure Pulse(const AObject: TObject); overload; static;
class procedure PulseAll(const AObject: TObject); overload; static;
class property DefaultSpinCount: Integer read FDefaultSpinCount write SetDefaultSpinCount;
end;
C++
struct DECLSPEC_DRECORD TMonitor
{
private:
struct TWaitingThread;
typedef TWaitingThread *PWaitingThread;
struct DECLSPEC_DRECORD TWaitingThread
{
public:
TMonitor::TWaitingThread *Next;
unsigned Thread;
void *WaitEvent;
};
struct DECLSPEC_DRECORD TSpinWait
{
private:
static const System::Int8 YieldThreshold = System::Int8(0xa);
static const System::Int8 Sleep1Threshold = System::Int8(0x14);
static const System::Int8 Sleep0Threshold = System::Int8(0x5);
int FCount;
public:
void __fastcall Reset(void);
void __fastcall SpinCycle(void);
};
struct DECLSPEC_DRECORD TSpinLock
{
private:
int FLock;
public:
void __fastcall Enter(void);
void __fastcall Exit(void);
};
private:
int FLockCount;
int FRecursionCount;
unsigned FOwningThread;
void *FLockEvent;
int FSpinCount;
TWaitingThread *FWaitQueue;
TSpinLock FQueueLock;
static int CacheLineSize;
static int FDefaultSpinCount;
static void __fastcall Spin(int Iterations);
static int __fastcall GetCacheLineSize();
void __fastcall QueueWaiter(TWaitingThread &WaitingThread);
void __fastcall RemoveWaiter(TWaitingThread &WaitingThread);
PWaitingThread __fastcall DequeueWaiter(void);
void * __fastcall GetEvent(void);
unsigned __fastcall CheckOwningThread(void);
static void __fastcall CheckMonitorSupport();
private:
static PMonitor __fastcall Create();
static void __fastcall Destroy(TObject* const AObject)/* overload */;
void __fastcall Destroy(void)/* overload */;
private:
static PPMonitor __fastcall GetFieldAddress(TObject* const AObject);
static PMonitor __fastcall GetMonitor(TObject* const AObject);
static void __fastcall SetDefaultSpinCount(int AValue);
bool __fastcall TryEnter(void)/* overload */;
bool __fastcall Wait(PMonitor ALock, unsigned Timeout)/* overload */;
void __fastcall Pulse(void)/* overload */;
void __fastcall PulseAll(void)/* overload */;
private:
bool __fastcall Enter(unsigned Timeout)/* overload */;
void __fastcall Exit(void)/* overload */;
public:
static void __fastcall SetSpinCount(TObject* const AObject, int ASpinCount);
static void __fastcall Enter(TObject* const AObject)/* overload */;
static bool __fastcall Enter(TObject* const AObject, unsigned Timeout)/* overload */;
static void __fastcall Exit(TObject* const AObject)/* overload */;
static bool __fastcall TryEnter(TObject* const AObject)/* overload */;
static bool __fastcall Wait(TObject* const AObject, unsigned Timeout)/* overload */;
static bool __fastcall Wait(TObject* const AObject, TObject* const ALock, unsigned Timeout)/* overload */;
static void __fastcall Pulse(TObject* const AObject)/* overload */;
static void __fastcall PulseAll(TObject* const AObject)/* overload */;
/* static */ __property int DefaultSpinCount = {read=FDefaultSpinCount, write=SetDefaultSpinCount};
};
Eigenschaften
Typ | Sichtbarkeit | Quelle | Unit | Übergeordnet |
---|---|---|---|---|
record struct |
public | System.pas System.hpp |
System | System |
Beschreibung
TMonitor stellt Methoden für die Synchronisierung des Zugriffs von mehreren Threads auf ein einzelnes Objekt bereit.
Mit den Klassenmethoden von TMonitor synchronisieren Sie den Zugriff von Threads auf Ressourcen in einer Multithread-Anwendung.