mormot.core.os--TSynLocker和TSynLocked

mormot.core.os--TSynLocker和TSynLocked

TLightLock

{ **************** TSynLocker/TSynLocked 和 低级线程特性 }

type
  /// 一个轻量级的独占非重入锁,存储在 PtrUInt 值中
  // - 在自旋一段时间后调用 SwitchToThread,但不使用任何读写操作系统API
  // - 警告:方法是非重入的,即在一个裸调用中两次调用 Lock 会导致死锁:
  //   对于需要重入方法的情况,请使用 TRWLock 或 TSynLocker/TOSLock
  // - 多个轻量级锁,每个保护少量变量(如列表),可能比更全局的 TOSLock/TRWLock 更高效
  // - 我们的轻量级锁预计保持时间非常短(几个CPU周期):
  //   如果锁可能阻塞太长时间,请使用 TSynLocker 或 TOSLock
  // - TryLock/UnLock 可用于线程安全地获取共享资源
  // - 在 CPU32 上占用 4 字节,在 CPU64 上占用 8 字节
  {$ifdef USERECORDWITHMETHODS}
  TLightLock = record
  {$else}
  TLightLock = object
  {$endif USERECORDWITHMETHODS}
  private
    Flags: PtrUInt; // 标志位
    // 由 Lock 方法在内联时调用的低级函数
    procedure LockSpin;
  public
    /// 如果实例未被初始化为 0,则调用此方法
    // - 例如,如果 TLightLock 被定义为类字段,则不需要此方法
    procedure Init;
      {$ifdef HASINLINE} inline; {$endif}
  
    /// 可用于将实例作为 TOSLock 进行终结处理
    // - 不执行任何操作 - 仅与 TOSLock 兼容
    procedure Done;
      {$ifdef HASINLINE} inline; {$endif}
  
    /// 进入独占非重入锁
    procedure Lock;
      {$ifdef HASINLINE} inline; {$endif}
  
    /// 尝试进入独占非重入锁
    // - 如果返回 true,则调用者最终应调用 UnLock()
    // - 也可用于线程安全地获取共享资源
    function TryLock: boolean;
      {$ifdef HASINLINE} inline; {$endif}
  
    /// 检查独占非重入锁是否已被获取
    function IsLocked: boolean;
      {$ifdef HASINLINE} inline; {$endif}
  
    /// 离开独占非重入锁
    procedure UnLock;
      {$ifdef HASINLINE} inline; {$endif}
  end;

TRWLightLock


  /// 一个轻量级的支持多个读操作/独占写操作的非升级锁
  // - 在自旋一段时间后调用 SwitchToThread,但不使用任何读写操作系统API
  // - 警告:读锁是可重入的并允许并发访问,但在读锁内部或另一个写锁内部调用 WriteLock 会导致死锁
  // - 如果您需要一个可升级的锁,请考虑使用 TRWLock - 但对于大多数读操作,
  //   TRWLightLock.ReadLock/ReadUnLock/WriteLock 模式比升级更快
  // - 我们的轻量级锁预计保持时间非常短(几个CPU周期):
  //   如果锁可能阻塞太长时间,请使用 TSynLocker 或 TOSLock
  // - 多个轻量级锁,每个保护少量变量(如列表),可能比更全局的 TOSLock/TRWLock 更高效
  // - 在 CPU32 上占用 4 字节,在 CPU64 上占用 8 字节
  {$ifdef USERECORDWITHMETHODS}
  TRWLightLock = record
  {$else}
  TRWLightLock = object
  {$endif USERECORDWITHMETHODS}
  private
    Flags: PtrUInt; // 标志位,位 0 = 写锁,>0 = 读锁计数器
    // 由 Lock 方法在内联时调用的低级函数
    procedure ReadLockSpin;
    procedure WriteLockSpin;
  public
    /// 如果实例未被初始化为 0,则调用此方法
    // - 例如,如果 TRWLightLock 被定义为类字段,则不需要此方法
    procedure Init;
      {$ifdef HASINLINE} inline; {$endif}
  
    /// 进入不可升级的多读锁
    // - 读锁维护一个线程安全的计数器,因此是可重入和非阻塞的
    // - 警告:在读锁之后嵌套调用 WriteLock 会导致死锁
    procedure ReadLock;
      {$ifdef HASINLINE} inline; {$endif}
  
    /// 尝试进入不可升级的多读锁
    // - 如果返回 true,则调用者最终应调用 ReadUnLock
    // - 读锁维护一个线程安全的计数器,因此是可重入和非阻塞的
    // - 警告:在读锁之后嵌套调用 WriteLock 会导致死锁
    function TryReadLock: boolean;
      {$ifdef HASINLINE} inline; {$endif}
  
    /// 离开不可升级的多读锁
    procedure ReadUnLock;
      {$ifdef HASINLINE} inline; {$endif}
  
    /// 进入不可重入且不可升级的独占写锁
    // - 警告:在读锁或另一个写锁之后嵌套调用 WriteLock 会导致死锁
    procedure WriteLock;
      {$ifdef HASINLINE} inline; {$endif}
  
    /// 尝试进入不可重入且不可升级的独占写锁
    // - 如果返回 true,则调用者最终应调用 WriteUnLock
    // - 警告:在读锁或另一个写锁之后嵌套调用 TryWriteLock 会导致死锁
    function TryWriteLock: boolean;
      {$ifdef HASINLINE} inline; {$endif}
  
    /// 离开不可重入且不可升级的独占写锁
    procedure WriteUnLock;
      {$ifdef HASINLINE} inline; {$endif}
  end;

TLockedList

/// 指向TLockedList中一个数据条目的指针
PLockedListOne = ^TLockedListOne;

/// TLockedList中一个数据条目的抽象父类,存储两个PLockedListOne指针
// - TLockedList应该存储以这些字段开头的非托管记录
// - sequence字段包含一个递增的、基于随机种子的30位整数(大于65535),
//   以避免实例回收时出现的ABA问题
TLockedListOne = record
    next, prev: pointer;  // 指向下一个和上一个条目的指针
    sequence: PtrUInt;    // 序列号,用于解决ABA问题
end;

/// 用于终结一个TLockedListOne实例的可选回调事件
TOnLockedListOne = procedure(one: PLockedListOne) of object;

/// 线程安全的双链表,包含TLockedListOne后代的回收机制
{$ifdef USERECORDWITHMETHODS}
TLockedList = record
{$else}
TLockedList = object
{$endif USERECORDWITHMETHODS}
private
    fHead, fBin: pointer;  // 分别指向链表头部和回收箱的指针
    fSize: integer;        // 列表中每个实例的大小(包括TLockedListOne头部)
    fSequence: PtrUInt;    // 全局序列号生成器
    fOnFree: TOnLockedListOne; // 当实例被释放时调用的回调
public
    /// 线程安全的访问锁
    Safe: TLightLock;
  
    /// 当前列表中存储的TLockedListOne实例数量(不包括回收箱中的实例)
    Count: integer;
  
    /// 初始化继承自TLockedListOne的大小的存储
    procedure Init(onesize: PtrUInt; const onefree: TOnLockedListOne = nil);
  
    /// 释放所有存储的内存
    procedure Done;
  
    /// 在线程安全的O(1)过程中分配一个新的PLockedListOne数据实例
    function New: pointer;
  
    /// 在线程安全的O(1)过程中释放一个已使用的PLockedListOne数据实例
    function Free(one: pointer): boolean;
  
    /// 释放当前存储在此列表中的所有TLockedListOne实例
    // - 不会将任何实例移动到内部回收箱
    procedure Clear;
  
    /// 释放内部回收箱中所有待回收的项
    // - 返回从内部收集器中释放了多少项
    function EmptyBin: integer;
  
    /// 作为PLockedListOne双链表的原始访问存储的项
    property Head: pointer
      read fHead;
  
    /// 存储的每个实例的大小,包括其TLockedListOne头部
    property Size: integer
      read fSize;
end;

TSynLocker

type  
  /// TSynLocker处理线程同步的方式  
  // - 默认情况下,uSharedLock将使用主TRTLCriticalSection  
  // - 您可以设置uRWLock并调用重载的RWLock/RWUnLock()来使用更轻量级的TRWLock - 但请注意,cReadOnly后跟cReadWrite/cWrite会导致死锁 - 常规的Lock/UnLock将使用cWrite独占锁  
  // - uNoLock将禁用整个锁定机制  
  TSynLockerUse = (  
    uSharedLock,  
    uRWLock,  
    uNoLock);  
  
  /// 允许向任何类实例添加跨平台锁定方法  
  // - 典型用途是定义一个Safe: TSynLocker属性,在构造函数/析构函数方法中调用Safe.Init和Safe.Done,并在try ... finally部分使用Safe.Lock/UnLock方法  
  // - 相对于TCriticalSection类,修复了可能降低多线程性能的CPU缓存行冲突问题,如http://www.delphitools.info/2011/11/30/fixing-tcriticalsection所述  
  // - 内部填充用于安全存储最多7个受互斥锁保护的值,因此SizeOf(TSynLocker)>128  
  // - 对于对象级锁定,请参阅TSynPersistentLock,它拥有一个此类实例,或在构造函数中调用低级fSafe := NewSynLocker,然后在析构函数中调用fSafe^.DoneAndFreemem  
  // - RWUse属性可以将TRTLCriticalSection替换为更轻量级的TRWLock  
  // - 如果多读/独占写锁更合适(仅当锁定过程不会花费太多时间时),请参阅TRWLock和TSynPersistentRWLock  
  {$ifdef USERECORDWITHMETHODS}  
  TSynLocker = record  
  {$else}  
  TSynLocker = object  
  {$endif USERECORDWITHMETHODS}  
  private  
    fSection: TRTLCriticalSection; // 主同步对象  
    fRW: TRWLock; // 可选的读写锁  
    fPaddingUsedCount: byte; // 填充区已使用计数  
    fInitialized: boolean; // 初始化标志  
    fRWUse: TSynLockerUse; // 锁使用模式  
    fLockCount: integer; // 锁计数(用于重入)  
    // 以下为用于安全访问内部填充数据的辅助方法  
    function GetVariant(Index: integer): Variant;  
    procedure SetVariant(Index: integer; const Value: Variant);  
    function GetInt64(Index: integer): Int64;  
    procedure SetInt64(Index: integer; const Value: Int64);  
    function GetBool(Index: integer): boolean;  
    procedure SetBool(Index: integer; const Value: boolean);  
    function GetUnlockedInt64(Index: integer): Int64;  
    procedure SetUnlockedInt64(Index: integer; const Value: Int64);  
    function GetPointer(Index: integer): Pointer;  
    procedure SetPointer(Index: integer; const Value: Pointer);  
    function GetUtf8(Index: integer): RawUtf8;  
    procedure SetUtf8(Index: integer; const Value: RawUtf8);  
    function GetIsLocked: boolean;  
    // - 如果RWUse=uSharedLock,则调用EnterCriticalSection(不支持并行读取)  
    // - 警告:如果RWUse=uRWLock,则此方法将使用内部TRWLock  
    // - 在受保护部分中定义,以便更好地内联并修复Delphi编译器关于uses classes中缺少Windows单元的警告  
    procedure RWLock(context: TRWLockContext);  
      {$ifdef HASINLINE} inline; {$endif}  
    procedure RWUnLock(context: TRWLockContext);  
      {$ifdef HASINLINE} inline; {$endif}  
  public  
    /// 内部填充数据,也用于存储最多7个Variant值  
    // - 这个内存缓冲区将确保不会发生CPU缓存行混合  
    // - 您不应直接使用此字段,而应使用Locked[], LockedInt64[], LockedUtf8[]或LockedPointer[]方法  
    // - 如果您要访问这些数组值,请确保在try ... finally结构中保护它们,并使用Safe.Lock和Safe.Unlock,同时准确维护PaddingUsedCount属性  
    Padding: array[0..6] of TVarData;  
  
    /// 初始化互斥锁  
    // - 调用此方法是强制性的(例如,在拥有TSynLocker实例的类构造函数中),否则您可能会遇到意外的行为,如访问违规或内存泄漏  
    procedure Init;  
   
    /// 终结互斥锁  
    // - 调用此方法是强制性的(例如,在拥有TSynLocker实例的类析构函数中),否则您可能会遇到意外的行为,如访问违规或内存泄漏  
    procedure Done;  
  
    /// 终结互斥锁,并对实例的指针调用FreeMem()  
    // - 应该通过NewSynLocker调用来初始化  
    procedure DoneAndFreeMem;  
  
    /// 低级锁定获取,相当于RWLock(cReadOnly)  
    // - 如果RWUse=uSharedLock,则调用EnterCriticalSection(不支持并行读取)  
    // - 警告:如果RWUse=uRWLock,则嵌套的Lock调用会导致死锁,但嵌套的ReadLock调用不会  
    procedure ReadLock;  
  
    /// 低级锁定释放,相当于RWUnLock(cReadOnly)  
    procedure ReadUnLock;  
  
    /// 低级锁定获取,相当于RWLock(cReadWrite)  
    // - 如果RWUse=uSharedLock,则调用EnterCriticalSection(不支持并行读取)  
    // - 如果RWUse=uRWLock,则嵌套的Lock调用不会导致死锁  
    procedure ReadWriteLock;  
    /// 低级锁定释放,相当于RWUnLock(cReadWrite)  
    procedure ReadWriteUnLock;  
  
    /// 对实例进行独占访问锁定,相当于RWLock(cWrite)  
    // - 从同一线程是可重入的,即您可以嵌套Lock/UnLock调用  
    // - 警告:如果RWUse=uRWLock,则在嵌套ReadLock之后会导致死锁,但在ReadWriteLock之后不会  
    // - 使用此类结构以避免竞态条件(从Safe: TSynLocker属性):  
    // ! Safe.Lock;  
    // ! try  
    // !   ...  
    // ! finally  
    // !   Safe.Unlock;  
    // ! end;  
    procedure Lock;  
  
    /// 尝试获取互斥锁  
    // - 如果RWUse不是默认的uSharedLock,则什么也不做并返回false  
    // - 使用此类结构以避免竞态条件(从Safe: TSynLocker属性):  
    // ! if Safe.TryLock then  
    // !   try  
    // !     ...  
    // !   finally  
    // !     Safe.Unlock;  
    // !   end;  
    function TryLock: boolean;  
  
    /// 尝试在给定的时间内获取互斥锁  
    // - 如果RWUse不是默认的uSharedLock,则只是等待并返回false  
    // - 使用此类结构以避免竞态条件(从Safe: TSynLocker属性):  
    // ! if Safe.TryLockMS(100) then  
    // !   try  
    // !     ...  
    // !   finally  
    // !     Safe.Unlock;  
    // !   end;  
    function TryLockMS(retryms: integer; terminated: PBoolean = nil): boolean;  
  
    /// 释放实例的独占访问权,相当于RWUnLock(cWrite)  
    // - 每个Lock/TryLock调用都应该有一个对应的UnLock调用,因此try..finally块是安全代码所必需的  
    procedure UnLock; overload;  
    /// 将进入互斥锁,直到释放IUnknown引用  
    // - 在Delphi中可以这样使用:  
    // !begin  
    // !  ... // 不安全代码  
    // !  Safe.ProtectMethod;  
    // !  ... // 线程安全代码  
    // !end; // 局部变量隐藏的IUnknown将释放方法的锁  
    // - 警告:在FPC中,您应该将结果分配给局部变量 - 请参阅bug http://bugs.freepascal.org/view.php?id=26602  
    // !var  
    // !  LockFPC: IUnknown;  
    // !begin  
    // !  ... // 不安全代码  
    // !  LockFPC := Safe.ProtectMethod;  
    // !  ... // 线程安全代码  
    // !end; // LockFPC将释放方法的锁  
    // 或  
    // !begin  
    // !  ... // 不安全代码  
    // !  with Safe.ProtectMethod do  
    // !  begin  
    // !    ... // 线程安全代码  
    // !  end; // 局部变量隐藏的IUnknown将释放方法的锁  
    // !end;  
    function ProtectMethod: IUnknown;  
  
    /// 存储在内部Padding[]数组中的值数  
    // - 如果没有存储任何值,则为0,否则为1..7之间的数字  
    // - 您通常不需要使用此字段,但对于在Lock/UnLock安全块内优化对Padding[]值的低级直接访问,它是必要的  
    property PaddingUsedCount: byte  
      read fPaddingUsedCount write fPaddingUsedCount;  
  
    /// 如果互斥锁当前被另一个线程锁定,则返回true  
    // - 如果RWUse=uRWLock,则任何锁(即使是ReadOnlyLock)也会返回true
    property IsLocked: boolean
      read GetIsLocked;
  
    /// 如果已为此互斥锁调用Init方法,则返回true
    // - 仅当整个对象之前已被填充为0时(即作为类的一部分或作为全局变量),这一点才相关,但如果是在堆栈上分配的,则可能不准确
    property IsInitialized: boolean
      read fInitialized;
  
    /// 安全锁定访问Variant值
    // - 您可以存储最多7个变量,使用0..6索引,与LockedBool、LockedInt64、LockedPointer和LockedUtf8数组属性共享
    // - 如果索引超出范围,则返回null
    // - 如果RWUse设置为uRWLock,则允许并发线程读取
    property Locked[Index: integer]: Variant
      read GetVariant write SetVariant;
  
    /// 安全锁定访问Int64值
    // - 您可以存储最多7个变量,使用0..6索引,与Locked和LockedUtf8数组属性共享
    // - Int64将作为varInt64变体内部存储
    // - 如果索引超出范围或不存储Int64,则返回nil
    // - 如果RWUse设置为uRWLock,则允许并发线程读取
    property LockedInt64[Index: integer]: Int64
      read GetInt64 write SetInt64;
  
    /// 安全锁定访问布尔值
    // - 您可以存储最多7个变量,使用0..6索引,与Locked、LockedInt64、LockedPointer和LockedUtf8数组属性共享
    // - 值将作为varboolean变体内部存储
    // - 如果索引超出范围或不存储布尔值,则返回nil
    // - 如果RWUse设置为uRWLock,则允许并发线程读取
    property LockedBool[Index: integer]: boolean
      read GetBool write SetBool;
   
    /// 安全锁定访问指针/TObject值
    // - 您可以存储最多7个变量,使用0..6索引,与Locked、LockedBool、LockedInt64和LockedUtf8数组属性共享
    // - 指针将作为varUnknown变体内部存储
    // - 如果索引超出范围或不存储指针,则返回nil
    // - 如果RWUse设置为uRWLock,则允许并发线程读取
    property LockedPointer[Index: integer]: Pointer
      read GetPointer write SetPointer;
  
    /// 安全锁定访问UTF-8字符串值
    // - 您可以存储最多7个变量,使用0..6索引,与Locked和LockedPointer数组属性共享
    // - UTF-8字符串将作为varString变体内部存储
    // - 如果索引超出范围或不存储字符串,则返回''
    // - 如果RWUse设置为uRWLock,则允许并发线程读取
    property LockedUtf8[Index: integer]: RawUtf8
      read GetUtf8 write SetUtf8;
  
    /// 安全锁定就地递增Int64值
    // - 您可以存储最多7个变量,使用0..6索引,与Locked和LockedUtf8数组属性共享
    // - Int64将作为varInt64变体内部存储
    // - 返回新存储的值
    // - 如果内部值尚未定义,则默认使用0
    function LockedInt64Increment(Index: integer; const Increment: Int64): Int64;
  
    /// 安全锁定就地交换Variant值
    // - 您可以存储最多7个变量,使用0..6索引,与Locked和LockedUtf8数组属性共享
    // - 返回之前存储的值,如果索引超出范围,则返回null
    function LockedExchange(Index: integer; const Value: variant): variant;
  
    /// 安全锁定就地交换指针/TObject值
    // - 您可以存储最多7个变量,使用0..6索引,与Locked和LockedUtf8数组属性共享
    // - 指针将作为varUnknown变体内部存储
    // - 返回之前存储的值,如果索引超出范围或不存储指针,则返回nil
    function LockedPointerExchange(Index: integer; Value: pointer): pointer;
  
    /// 不安全访问Int64值
    // - 您可以存储最多7个变量,使用0..6索引,与Locked和LockedUtf8数组属性共享
    // - Int64将作为varInt64变体内部存储
    // - 如果索引超出范围或不存储Int64,则返回nil
    // - 您应该调用LockedInt64[]属性,或使用此属性并在Lock; try ... finally UnLock块中使用
    property UnlockedInt64[Index: integer]: Int64
      read GetUnlockedInt64 write SetUnlockedInt64;
  
    /// RWLock/RWUnLock的处理方式
    property RWUse: TSynLockerUse
      read fRWUse write fRWUse;
  end;

这段代码定义了一个 TSynLocker类型,它允许跨平台地为任何类实例添加锁定方法,以确保线程安全。它提供了多种锁定机制(如共享锁、读写锁和无锁),以及安全访问内部存储的值的方法。这些特性使得 TSynLocker成为处理多线程编程中同步问题的一个强大工具。

TAutoLock

/// 指向TSynLocker互斥锁实例的指针
// - 另请参见NewSynLocker和TSynLocker.DoneAndFreemem函数
PSynLocker = ^TSynLocker;

/// TAutoLocker.ProtectMethod和TSynLocker.ProtectMethod使用的原始类
// - 在此定义以供mormot.core.data.pas中的TAutoLocker使用
TAutoLock = class(TInterfacedObject)
protected
    fLock: PSynLocker; // 指向TSynLocker的指针
public
    constructor Create(aLock: PSynLocker); // 构造函数,接受一个PSynLocker参数
    destructor Destroy; override; // 析构函数,重写自TInterfacedObject
end;

TSynEvent


/// 我们的轻量级跨平台TEvent类似组件
// - 在Windows上,直接调用CreateEvent/ResetEvent/SetEvent API
// - 在Linux上,将使用阻塞和非信号量模式的eventfd()
// - 在其他POSIX系统上,将使用比TEvent BasicEvent更轻的PRTLEvent
// - 唯一限制是我们不知道WaitFor是被信号触发还是超时,
// 但实际上这并不是一个大问题,因为大多数代码不需要这个信息
// 或者已经在其实现逻辑中有了自己的标志
TSynEvent = class
protected
    fHandle: pointer; // Windows THandle或FPC PRTLEvent
    fFD: integer;     // 用于eventfd()
public
    /// 初始化跨平台事件实例
    constructor Create;
    /// 终结跨平台事件实例
    destructor Destroy; override;
    /// 忽略任何挂起的事件,以便WaitFor将在下次SetEvent时被设置
    procedure ResetEvent;
      {$ifdef OSPOSIX} inline; {$endif}
    /// 触发任何挂起的事件,释放WaitFor/WaitForEver方法
    procedure SetEvent;
      {$ifdef OSPOSIX} inline; {$endif}
    /// 等待,直到另一个线程调用SetEvent,具有最大时间
    // - 如果被信号触发或超时,则不返回
    // - 警告:您应该一次只从一个线程等待
    procedure WaitFor(TimeoutMS: integer);
      {$ifdef OSPOSIX} inline; {$endif}
    /// 无限期等待,直到另一个线程调用SetEvent
    procedure WaitForEver;
      {$ifdef OSPOSIX} inline; {$endif}
    /// 在检查终止标志和此事件的同时,分步骤调用SleepHiRes()
    function SleepStep(var start: Int64; terminated: PBoolean): Int64;
    /// 如果使用了eventfd() API,则可用于调整算法
    function IsEventFD: boolean;
      {$ifdef HASINLINE} inline; {$endif}
end;

NewSynLocker

/// 从堆初始化一个TSynLocker实例
// - 调用DoneandFreeMem来释放相关内存和操作系统互斥锁
// - 例如,在TSynPersistentLock中使用以减少类实例大小
function NewSynLocker: PSynLocker;

TSynLocked

type
  {$M+} // 开启内存管理消息

  /// TSynPersistentLock的一个持久性无关替代方案
  // - 当不需要自定义JSON持久性时,可以用作基类
  // - 可以考虑将TRWLock字段用作更轻量级的多读/独占写选项
  TSynLocked = class
  protected
    fSafe: PSynLocker; // TSynLocker会增加继承字段的偏移量
  public
    /// 初始化实例及其关联的锁
    // - 定义为virtual,就像TObjectWithCustomCreate/TSynPersistent一样
    constructor Create; virtual;
    /// 终结实例及其关联的锁
    destructor Destroy; override;
    /// 访问关联的实例临界区
    // - 调用Safe.Lock/UnLock来保护此存储的多线程访问
    property Safe: PSynLocker
      read fSafe;
  end;

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

  /// TSynLocked层次的元类定义
  TSynLockedClass = class of TSynLocked;

TLecuyerThreadSafe


  /// 线程安全的Pierre L'Ecuyer软件随机数生成器
  // - 仅用TLightLock包装TLecuyer
  // - 除非可能比threadvar稍快,否则不应使用
  {$ifdef USERECORDWITHMETHODS}
  TLecuyerThreadSafe = record
  {$else}
  TLecuyerThreadSafe = object
  {$endif USERECORDWITHMETHODS}
  public
    Safe: TLightLock;
    Generator: TLecuyer;
    /// 计算下一个生成的32位值
    function Next: cardinal; overload;
    /// 计算一个64位浮点数
    function NextDouble: double;
    /// 用随机字节异或某个内存缓冲区
    procedure Fill(dest: pointer; count: integer);
    /// 用7位ASCII随机文本填充某个string[31]
    procedure FillShort31(var dest: TShort31);
  end;

  TThreadIDDynArray = array of TThreadID; // 线程ID动态数组类型



var
  /// 全局线程安全的Pierre L'Ecuyer软件随机数生成器
  // - 除非可能比threadvar稍快,否则不应使用
  SharedRandom: TLecuyerThreadSafe;

与线程、CPU核心和事件相关的类型和函数


{$ifdef OSPOSIX}
  /// 可设置为TRUE,以强制SleepHiRes(0)调用POSIX sched_yield
  // - 在实践中,据报道在POSIX系统上存在问题
  // - 即使是Linus Torvalds本人也对它的使用表示愤怒 - 例如,请参见
  // https://www.realworldtech.com/forum/?threadid=189711&curpostid=189752
  // - 您可以自己尝试它
  SleepHiRes0Yield: boolean = false;
{$endif OSPOSIX}

/// 类似于Windows的sleep() API调用,真正实现跨平台
// - 使用毫秒级分辨率
// - SleepHiRes(0)在Windows上调用ThreadSwitch,但在POSIX版本中将等待10微秒
// 除非强制SleepHiRes0Yield为true(坏主意)
// - 相对于RTL的Sleep()函数,如果在任何OS信号中断时返回ESysEINTR
// - 警告:通常在Windows上等待下一个系统计时器中断,默认为每16毫秒一次;
// 因此,永远不要依赖提供的毫秒值来猜测经过的时间,而应调用GetTickCount64
procedure SleepHiRes(ms: cardinal); overload;

/// 类似于Windows的sleep() API调用,但真正实现跨平台
// 并在等待期间检查Terminated标志以快速响应中止
// - 如果terminated^被设置为true(terminatedvalue),则返回true
function SleepHiRes(ms: cardinal; var terminated: boolean;
  terminatedvalue: boolean = true): boolean; overload;

/// 调用SleepHiRes(),考虑活动的步长,在0/1/5/50/120-250毫秒步长中
// - 范围设计激进,以响应性为代价燃烧一些CPU
// - 当发生某些活动时,应重置start := 0,或在Windows上设置start := -1
// 以避免任何SleepHiRes(0) = SwitchToThread调用
// - 可选地在terminated^被设置或事件被信号触发时返回
// - 返回当前的GetTickCount64值
function SleepStep(var start: Int64; terminated: PBoolean = nil): Int64;

/// 计算最佳睡眠时间作为0/1/5/50然后120-250毫秒步长
// - 范围设计激进,以响应性为代价燃烧一些CPU
function SleepDelay(elapsed: PtrInt): PtrInt;

/// 计算最佳睡眠时间,类似于SleepStep,在0/1/5/50/120-250毫秒步长中
// - 范围设计激进,以响应性为代价燃烧一些CPU
// - start=0将用tix填充其值,start<0将用tix-50填充其值
// 以便SleepDelay()永远不会调用SleepHiRes(0)
function SleepStepTime(var start, tix: Int64; endtix: PInt64 = nil): PtrInt;

/// 类似于Windows的SwitchToThread API调用,真正实现跨平台
// - 在POSIX系统上调用fpnanosleep(10),或在Windows上调用同名API
procedure SwitchToThread;
  {$ifdef OSWINDOWS} stdcall; {$endif}

/// 在循环中尝试LockedExc(),在自旋后调用SwitchToThread
procedure SpinExc(var Target: PtrUInt; NewValue, Comperand: PtrUInt);

/// 包装器,用于实现线程安全的T*ObjArray动态数组存储
function ObjArrayAdd(var aObjArray; aItem: TObject;
  var aSafe: TLightLock; aCount: PInteger = nil): PtrInt; overload;

/// 包装器,用于实现线程安全的指针动态数组存储
function PtrArrayDelete(var aPtrArray; aItem: pointer; var aSafe: TLightLock;
  aCount: PInteger = nil): PtrInt; overload;

/// 尝试杀死/取消一个线程
// - 在Windows上,调用TerminateThread() API
// - 在Linux/FPC下,调用异步的pthread_cancel() API
function RawKillThread(Thread: TThread): boolean;

type
  /// 存储逻辑CPU核心的位掩码,由SetThreadMaskAffinity使用
  // - 在Windows上为32/64位指针大小,在POSIX上为1024位
  TCpuSet = {$ifdef OSWINDOWS} PtrUInt {$else} array[0..127] of byte {$endif};

var
  /// 每个硬件CPU插槽托管的逻辑CPU核心的底层位掩码
  // - 在进程启动时填充为CpuSocketsMask[0 .. CpuSockets - 1]范围
  CpuSocketsMask: array of TCpuSet;

/// 用零填充CPU核心的位掩码
procedure ResetCpuSet(out CpuSet: TCpuSet);
  {$ifdef HASINLINE} inline; {$endif}

/// 在CPU核心的位掩码中设置特定的位
function SetCpuSet(var CpuSet: TCpuSet; CpuIndex: cardinal): boolean;

/// 检索系统当前可用的CPU核心位掩码
// - 当前进程可能已被调整为仅使用核心的一个子集
// 例如,通过Linux上的"taskset -c"
// - 返回可访问的CPU核心数量 - 即GetBitsCount(CpuSet)或
// 如果函数失败则为0
function CurrentCpuSet(out CpuSet: TCpuSet): integer;

/// 尝试将给定线程分配给特定的逻辑CPU核心集
// - 在Windows上,调用SetThreadAffinityMask() API
// - 在Linux/FPC下,调用pthread_setaffinity_np() API
function SetThreadMaskAffinity(Thread: TThread; const Mask: TCpuSet): boolean;

/// 尝试将给定线程分配给特定的逻辑CPU核心
// - CpuIndex应在0 .. SystemInfo.dwNumberOfProcessors - 1范围内
function SetThreadCpuAffinity(Thread: TThread; CpuIndex: cardinal): boolean;

/// 尝试将给定线程分配给特定的硬件CPU插槽
// - SocketIndex应在0 .. CpuSockets - 1范围内,并将使用
// 在进程启动时检索的CpuSocketsMask[]信息
function SetThreadSocketAffinity(Thread: TThread; SocketIndex: cardinal): boolean;

/// 线程的底层命名
// - 在Windows上,将引发一个标准的“假”异常来通知线程名称
// - 在Linux/FPC下,调用pthread_setname_np() API,该API将名称截断为16个字符
procedure RawSetThreadName(ThreadID: TThreadID; const Name: RawUtf8);

/// 为当前线程命名,以便在IDE调试器中轻松识别
// - 可以通过CurrentThreadNameShort/GetCurrentThreadName检索
// - 只是SetThreadName(GetCurrentThreadId, ...)的包装器
procedure SetCurrentThreadName(const Format: RawUtf8; const Args: array of const); overload;

/// 为当前线程命名,以便在IDE调试器中轻松识别
// - 也可以通过CurrentThreadNameShort/GetCurrentThreadName检索
// - 只是SetThreadName(GetCurrentThreadId, ...)的包装器
procedure SetCurrentThreadName(const Name: RawUtf8); overload;

var
  /// 为线程命名,以便在IDE调试器中轻松识别
  // - 默认实现不执行任何操作,除非包含了mormot.core.log
  // - 如果在调试应用程序时此功能出现问题,可以通过设置NOSETTHREADNAME条件来强制此函数不执行任何操作
  // - 大多数无意义的模式(如'TSql')会被修剪以减少结果长度,这在POSIX截断到16个字符时很方便
  // - 您稍后可以使用CurrentThreadNameShort检索名称
  // - 此方法将注册TSynLog.LogThreadName(),因此调用它的线程也应该调用TSynLogFamily.OnThreadEnded/TSynLog.NotifyThreadEnded
  SetThreadName: procedure(ThreadID: TThreadID; const Format: RawUtf8;
    const Args: array of const);

/// 低级访问由SetThreadName()设置的线程名称
// - 由于threadvar不能包含托管字符串,因此它被定义为TShort31,
// 因此限制为31个字符,这足够了,因为POSIX截断到16个字符,并且SetThreadName会修剪无意义的模式
function CurrentThreadNameShort: PShortString;

/// 检索由SetThreadName()设置的线程名称
// - 如果可能,直接调用CurrentThreadNameShort函数会稍微快一些
// - 将返回CurrentThreadNameShort^ threadvar的31个字符值
function GetCurrentThreadName: RawUtf8;

/// 返回线程ID和线程名称作为ShortString
// - 返回例如'Thread 0001abcd [shortthreadname]'
// - 用于在记录日志或引发异常时方便使用
function GetCurrentThreadInfo: ShortString;

/// 进入一个进程范围的巨型锁,用于线程安全的共享进程
// - 应受如下保护:
// ! GlobalLock;
// ! try
// !   .... 执行尽可能短的线程安全操作
// ! finally
// !   GlobalUnLock;
// ! end;
// - 最好不要使用这种巨型锁,而应使用专用的临界区/TSynLocker或TRWLock实例 - 这些函数仅为了方便,用于非时间关键型进程(例如,外部库的单例初始化,或在任何情况下都将使用它的RegisterGlobalShutdownRelease()之前)
procedure GlobalLock;

/// 释放线程安全的共享进程的巨型锁
procedure GlobalUnLock;

/// 框架将在此处注册一些实例以便最终释放
// - 在此根单元中比在每个finalization部分中更好
// - 其使用受到GlobalLock的保护
function RegisterGlobalShutdownRelease(Instance: TObject;
  SearchExisting: boolean = false): pointer;

这段代码定义了一系列与线程、CPU核心和事件相关的类型和函数。它涵盖了跨平台的线程管理、CPU亲和性设置、线程命名等功能。这些功能在多线程应用程序中非常有用,尤其是在需要精细控制线程行为和优化性能的场景中。

posted @ 2024-07-09 14:25  海利鸟  阅读(23)  评论(0编辑  收藏  举报