mormot.core.os--TSynLocker和TSynLocked

海利鸟發表於2024-07-09

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親和性設定、執行緒命名等功能。這些功能在多執行緒應用程式中非常有用,尤其是在需要精細控制執行緒行為和最佳化效能的場景中。

相關文章