mormot.core.threads--TSynQueue

来源:https://www.cnblogs.com/hieroly/p/18289754
-Advertisement-
Play Games

本書由Python pandas項目創始人Wes McKinney親筆撰寫,詳細介紹利用Python進行操作、處理、清洗和規整數據等方面的具體細節和基本要點。第2版針對Python 3.6進行全面修訂和更新,涵蓋新版的pandas、NumPy、IPython和Jupyter,並增加大量實際案例,可以 ...


mormot.core.threads--TSynQueue

以下是對 mormot.core.threads中部分代碼的翻譯,特別是關於 TSynQueue類的部分:

const
  // 在這裡定義以避免在uses子句中顯式鏈接到syncobjs單元
  wrSignaled = syncobjs.wrSignaled; // 等待結果:已發出信號
  wrTimeout  = syncobjs.wrTimeout;  // 等待結果:超時
  wrError    = syncobjs.wrError;    // 等待結果:錯誤

type
  // 在這裡定義以避免在uses子句中顯式鏈接到syncobjs單元
  // - 請註意,您可能更想使用來自mormot.core.os.pas的TSynEvent
  TWaitResult = syncobjs.TWaitResult; // 等待操作的結果類型

  // 在這裡定義以避免在uses子句中顯式鏈接到syncobjs單元
  // - 請註意,您可能更想使用來自mormot.core.os.pas的TSynEvent
  TEvent = syncobjs.TEvent; // 事件對象類型

{$endif PUREMORMOT2}

type
  // TThread的動態數組類型
  TThreadDynArray = array of TThread;

  // 由本單元引發的異常類
  ESynThread = class(ESynException);

{ ************ 線程安全的TSynQueue和TPendingTaskList }

type
  // 線程安全的FIFO(先進先出)記錄隊列
  // - 內部使用TDynArray存儲,採用滑動演算法,比FPC或Delphi的TQueue或簡單的TDynArray.Add/Delete更高效
  // - 如果需要,支持TSynPersistentStore二進位持久化
  // - 此結構也是線程安全的
  TSynQueue = class(TSynPersistentStore)
  protected
    // ...(省略了保護成員的詳細翻譯,它們主要是內部實現細節)
  public
    /// 初始化隊列存儲
    // - aTypeInfo應該是存儲在此TSynQueue實例中的值的動態數組TypeInfo() RTTI指針
    // - 可以選擇性地為此實例分配一個名稱
    constructor Create(aTypeInfo: PRttiInfo; const aName: RawUtf8 = ''); reintroduce; virtual;
    /// 釋放存儲
    // - 將釋放所有內部存儲的值,並調用WaitPopFinalize
    destructor Destroy; override;
    /// 將一個項目推入隊列
    // - 此方法是線程安全的,因為它會鎖定實例
    procedure Push(const aValue);
    /// 從隊列中提取一個項目,作為FIFO(先進先出)
    // - 如果aValue已被填充為掛起的項目,則返回true,並且該項目從隊列中移除(如果不想移除,請使用Peek)
    // - 如果隊列為空,則返回false
    // - 此方法是線程安全的,因為它會鎖定實例
    function Pop(out aValue): boolean;
    /// 從隊列中提取一個匹配的項目,作為FIFO(先進先出)
    // - 將當前掛起的項目與aAnother值進行比較
    function PopEquals(aAnother: pointer; aCompare: TDynArraySortCompare;
      out aValue): boolean;
    /// 從隊列中查找一個項目,作為FIFO(先進先出)
    // - 如果aValue已被填充為掛起的項目,則返回true,並且該項目不會從隊列中移除(與Pop方法不同)
    // - 如果隊列為空,則返回false
    // - 此方法是線程安全的,因為它會鎖定實例
    function Peek(out aValue): boolean;
    /// 等待並從隊列中提取一個項目,作為FIFO(先進先出)
    // - 如果在指定的aTimeoutMS時間內aValue已被填充為掛起的項目,則返回true
    // - 如果沒有在時間內將項目推入隊列,或者已調用WaitPopFinalize,則返回false
    // - aWhenIdle可用於空閑時處理消息,例如VCL/LCL的Application.ProcessMessages
    // - 您可以選擇在返回之前比較掛起的項目(當多個線程將項目放入隊列時可能很有用)
    // - 此方法是線程安全的,但僅在需要時鎖定實例
    function WaitPop(aTimeoutMS: integer; const aWhenIdle: TThreadMethod;
      out aValue; aCompared: pointer = nil;
      aCompare: TDynArraySortCompare = nil): boolean;
    /// 在隊列中等待查找一個項目,作為FIFO(先進先出)
    // - 在aTimeoutMS時間內返回一個指向掛起項目的指針
    // - 保持Safe.ReadWriteLock,因此調用者可以檢查其內容,然後如果它是預期的,則調用Pop(),並最終調用Safe.ReadWriteUnlock
    // - 如果沒有在時間內將項目推入隊列,則返回nil
    // - 此方法是線程安全的,但僅在需要時鎖定實例
    function WaitPeekLocked(aTimeoutMS: integer;
      const aWhenIdle: TThreadMethod): pointer;
    /// 確保任何掛起或未來的WaitPop()立即返回false
    // - 總是由Destroy析構函數調用
    // - 也可以從例如UI的OnClose事件中調用,以避免任何鎖定
    // - 此方法是線程安全的,但僅在需要時鎖定實例
    procedure WaitPopFinalize(aTimeoutMS: integer = 100);
    /// 刪除此隊列中當前存儲的所有項目,並清空其容量
    // - 此方法是線程安全的,因為它會鎖定實例
    procedure Clear;
    /// 用存儲的隊列項目初始化一個動態數組
    // - aDynArrayValues應該是Create方法中aTypeInfo定義的變數
    // - 您可以檢索一個可選的TDynArray包裝器,例如用於二進位或JSON持久化
    // - 此方法是線程安全的,並將複製隊列數據
    procedure Save(out aDynArrayValues; aDynArray: PDynArray = nil); overload;
    /// 返回當前存儲在此隊列中的項目數
    // - 此方法不是線程安全的,因此返回的值應是指示性的,或者您應使用顯式的Safe鎖/解鎖
    // - 如果您想檢查隊列是否為空,請調用Pending
    function Count: integer;
    /// 返回當前在記憶體中保留的槽位數
    // - 隊列具有優化的自動調整大小演算法,您可以使用此方法返回其當前容量
    // - 此方法不是線程安全的,因此返回的值是指示性的
    function Capacity: integer;
    /// 如果隊列中有一些項目當前掛起,則返回true
    // - 比檢查Count=0更快,並且比Pop或Peek快得多
    // - 此方法不是線程安全的,因此返回的值是指示性的
    function Pending: boolean;
      {$ifdef HASINLINE}inline;{$endif}
  end;

這個翻譯提供了對 TSynQueue類及其成員、方法和屬性的概述,以便更好地理解其設計目的和使用方式。請註意,翻譯過程中省略了保護成員的詳細翻譯,因為它們主要是內部實現細節,對於外部使用來說不是必需的。

在Free Pascal環境下,使用 TSynQueue類的一個示例會涉及創建隊列實例、向隊列中添加元素、從隊列中提取元素,以及處理可能的併發訪問。由於 TSynQueue是線程安全的,因此它非常適合在多線程應用程式中使用。然而,為了簡化示例,我們將在一個單線程環境中展示其基本用法。

請註意,由於 TSynQueue可能是特定於某個庫(如mORMot)的,因此您可能需要確保該庫已被正確安裝並包含在您的項目中。以下是一個簡化的使用示例:

program TSynQueueExample;

{$MODE DELPHI}
{$APPTYPE CONSOLE}

uses
  SysUtils, // 包含WriteLn等標準輸出函數
  mormot.core.threads; 

type
  // 定義一個簡單的記錄類型,用於存儲在TSynQueue中
  TMyData = record
    ID: Integer;
    Value: String;
  end;

var
  Queue: TSynQueue;
  Data: TMyData;

begin
  try
    // 創建TSynQueue實例,傳遞TMyData類型的TypeInfo
    Queue := TSynQueue.Create(TypeInfo(TMyDataArray), 'MyDataQueue');
    try
      // 向隊列中添加數據
      Queue.Push(TMyData.Create(1, 'First'));
      Queue.Push(TMyData.Create(2, 'Second'));
      Queue.Push(TMyData.Create(3, 'Third'));

      // 註意:上面的Push調用實際上是有問題的,因為TMyData是一個記錄類型,
      // 它不是通過Create方法創建的。這裡只是為了演示如何調用Push。
      // 在實際使用中,您應該直接傳遞記錄的值,如下所示:
      // Queue.Push((ID: 1; Value: 'First')); // 但這取決於TSynQueue的實現是否支持記錄值傳遞

      // 由於記錄類型通常是通過值傳遞的,並且TSynQueue可能設計為存儲記錄的副本,
      // 因此您應該這樣做:
      Queue.Push((ID: 1, Value: 'First'));
      Queue.Push((ID: 2, Value: 'Second'));
      Queue.Push((ID: 3, Value: 'Third'));

      // 從隊列中提取數據(FIFO)
      while Queue.Pop(Data) do
      begin
        WriteLn('Popped Data: ID = ', Data.ID, ', Value = ', Data.Value);
      end;

      // 此時隊列應為空
      if not Queue.Pending then
        WriteLn('Queue is empty.');

    finally
      // 銷毀TSynQueue實例
      Queue.Free;
    end;
  except
    on E: Exception do
      WriteLn('Error: ', E.Message);
  end;
  WriteLn('Program ended.');
end.

重要註意事項

  1. 在上面的示例中,我使用了 TMyDataArray作為 TypeInfo的參數,但實際上 TypeInfo(TMyDataArray)可能不是有效的,因為 TMyDataArray在示例中並未定義。通常,您應該傳遞記錄類型本身的 TypeInfo,但 TSynQueue可能期望一個動態數組類型來存儲其元素。然而,由於 TSynQueue的設計允許它存儲記錄的副本(而不是指針),因此您可能不需要定義一個動態數組類型。在實際使用中,您應該查閱 TSynQueue的文檔以確定如何正確地傳遞 TypeInfo
  2. 記錄類型通常是通過值傳遞的,並且上面的 Push調用示例假設 TSynQueue能夠處理記錄值的直接傳遞。這取決於 TSynQueue的具體實現。如果 TSynQueue被設計為存儲指向記錄的指針,那麼您可能需要定義一個動態數組類型或使用其他機制來傳遞記錄。
  3. 由於 TSynQueue是線程安全的,因此在多線程環境中使用時,您不需要擔心併發訪問問題。但是,在上面的示例中,我們為了簡化而在一個單線程環境中展示了其基本用法。
  4. 請確保將 'YourSynapseUnit'替換為實際包含 TSynQueue定義的單元名稱。如果 TSynQueue是mORMot庫的一部分,那麼您可能需要包含mORMot的相應單元。

以下是對 TPendingTaskList及其相關類型的翻譯,包括其保護類型、構造函數、方法和屬性:

type
  /// 內部項定義,用於TPendingTaskList存儲
  // 該記錄定義了待執行任務的時間戳和任務內容(以RawByteString形式存儲)
  TPendingTaskListItem = packed record
    /// 當TPendingTaskList.GetTimestamp達到此值時,應執行該任務
    Timestamp: Int64;
    /// 與此時間戳相關聯的任務,以原始二進位字元串形式存儲
    Task: RawByteString;
  end;

  /// 內部列表定義,用於TPendingTaskList存儲
  // TPendingTaskListItem的動態數組
  TPendingTaskListItemDynArray = array of TPendingTaskListItem;

  /// 線程安全的任務列表,任務以RawByteString形式存儲,並帶有時間戳
  // - 您可以向內部列表添加任務,在給定延遲後執行,使用類似發佈/查看的演算法
  // - 執行延遲可能不准確,但會根據每次調用NextPendingTask和GetTimestamp的解析度進行最佳猜測
  TPendingTaskList = class
  protected
    // 內部存儲結構和同步訪問
    fTask: TPendingTaskListItemDynArray; // 存儲待執行任務的數組
    fTasks: TDynArrayLocked; // 對fTask數組的封裝,提供線程安全的訪問
    // 獲取當前存儲的任務數量(線程安全)
    function GetCount: integer;
    // 獲取當前時間戳(預設為GetTickCount64)
    function GetTimestamp: Int64; virtual;
  public
    // 初始化列表的記憶體和資源
    constructor Create; reintroduce;
    // 添加一個任務,指定從當前時間開始的延遲(毫秒)
    procedure AddTask(aMilliSecondsDelayFromNow: integer;
      const aTask: RawByteString); virtual;
    // 添加多個任務,指定任務之間的延遲(毫秒)
    // - 第一個提供的延遲將從當前時間開始計算,然後指定下一個提供的任務之間的等待時間
    // - 也就是說,aMilliSecondsDelays不是絕對延遲
    procedure AddTasks(const aMilliSecondsDelays: array of integer;
      const aTasks: array of RawByteString);
    // 檢索下一個待執行的任務
    // - 如果沒有在當前時間可用的計劃任務,則返回''
    // - 根據指定的延遲返回下一個任務
    function NextPendingTask: RawByteString; virtual;
    // 清空所有待執行的任務
    procedure Clear; virtual;
    // 訪問內部存儲的TPendingTaskListItem.Timestamp值
    // - 對應當前時間
    // - 預設實現返回GetTickCount64,在Windows下典型解析度為16毫秒
    property Timestamp: Int64 read GetTimestamp;
    // 當前定義了多少個待執行任務
    property Count: integer read GetCount;
    // 對內部任務列表的直接低級訪問
    // - 警告:此動態數組的長度是列表的容量:請使用Count屬性來檢索存儲的項的確切數量
    // - 使用Safe.Lock/TryLock與try ... finally Safe.Unlock塊進行線程安全的訪問
    // - 項按時間戳遞增存儲,即第一項是NextPendingTask方法將返回的下一個項
    property Task: TPendingTaskListItemDynArray read fTask;
  end;

TPendingTaskList類提供了一種機制來存儲和按計劃執行一系列任務,每個任務都與一個時間戳相關聯。通過調用 AddTaskAddTasks方法,您可以將任務添加到列表中,這些任務將在指定的延遲後執行。NextPendingTask方法用於檢索下一個待執行的任務,而 Clear方法用於清空整個任務列表。

註意,TPendingTaskList類中的 Timestamp屬性和 GetTimestamp方法是用於確定何時執行任務的關鍵。GetTimestamp方法預設返回 GetTickCount64的值,但在子類中可以根據需要進行重寫,以提供不同的時間戳生成邏輯。同樣,NextPendingTask方法也是虛擬的,允許在子類中實現自定義的任務檢索邏輯。

在Free Pascal環境下,結合 TPendingTaskList類的定義,我們可以編寫一個示常式序來展示這兩個類的基本用法。以下是一個簡化的示例,一個 TPendingTaskList實例來按計劃執行任務(在這個例子中,任務只是簡單地列印消息)。

請註意,由於 TPendingTaskList可能是特定於某個庫(如mORMot)的,因此您需要確保該庫已被正確安裝並包含在您的項目中。此外,為了簡化示例,我們將在一個單線程環境中運行它,儘管這些類設計用於多線程環境。

program PendingTaskListExample;

{$MODE DELPHI}
{$APPTYPE CONSOLE}

uses
  SysUtils, // 包含WriteLn等標準輸出函數
  YourSynapseUnit; // 替換為實際包含這些類定義的單元名稱

var
  Queue: TSynQueue;
  TaskList: TPendingTaskList;
  I: Integer;
  TaskMessage: RawByteString;

begin
  try
    // 創建TPendingTaskList實例來按計劃執行任務
    TaskList := TPendingTaskList.Create;
    try
      // 添加一些計劃任務到列表中
      // 假設每個任務只是列印一條消息,延遲從當前時間開始計算
      TaskList.AddTask(1000, 'Task 1 in 1 second'); // 1秒後執行
      TaskList.AddTask(2000, 'Task 2 in 2 seconds'); // 2秒後執行

      // 註意:由於這個示例是在單線程環境中運行的,
      // 我們不會等待任務實際執行。在實際應用中,
      // 您可能需要在另一個線程中調用NextPendingTask,
      // 或者使用某種形式的定時器或事件迴圈來檢查並執行任務。

      // 為了模擬任務執行,我們可以手動調用NextPendingTask
      // 並列印消息(但在實際應用中,這通常不是您想要的方式)
      repeat
        TaskMessage := TaskList.NextPendingTask;
        if TaskMessage <> '' then
          WriteLn('Executing Task: ', TaskMessage)
        else
          Break; // 沒有更多待執行的任務,退出迴圈

        // 在這裡,我們實際上應該等待一段時間再檢查下一個任務,
        // 但為了簡化示例,我們只是立即再次檢查(這不是實際用法)
      until False;

    finally
      // 銷毀TPendingTaskList實例(在這個簡單的示例中可能不是必需的,
      // 但為了完整性而包含)
      TaskList.Free;
    end;

  except
    on E: Exception do
      WriteLn('Error: ', E.Message);
  end;
  WriteLn('Program ended.');
end.

重要註意事項

  1. 單線程執行:上面的示例是在單線程環境中運行的,因此它不會按預期等待任務實際執行。在實際應用中,您應該在一個單獨的線程中或在事件迴圈中定期調用 NextPendingTask來檢查並執行任務。
  2. 模擬任務執行:為了簡化示例,我們手動調用了 NextPendingTask並立即列印了消息。在實際應用中,您應該根據 NextPendingTask的返回值來決定是否執行任務,並且您可能需要等待一段時間再檢查下一個任務。
  3. 替換單元名稱:請確保將 'YourSynapseUnit'替換為實際包含 TSynQueueTPendingTaskList類定義的單元名稱。
  4. 錯誤處理:示例中包含了基本的錯誤處理邏輯,但在實際應用中,您可能需要更詳細的錯誤處理和日誌記錄。
  5. 線程安全:儘管 TSynQueueTPendingTaskList是線程安全的,但在從多個線程訪問它們時,您仍然需要確保正確地同步對它們的訪問(儘管在這個簡單的示例中我們沒有這樣做)。在實際應用中,您可能需要使用鎖、信號量或其他同步機制來確保線程安全。然而,在這個特定的示例中,由於我們是在單線程環境中運行,因此不需要擔心線程安全問題。

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 本文探討了Node.js事件迴圈中的timers階段,分析了定時器的管理和執行過程。通過源碼解析,揭示了定時器超時檢查、回調執行以及定時器類型(setTimeout與setInterval)的內部實現機制。文章旨在幫助讀者理解Node.js中定時器的工作原理及其在事件驅動編程中的重要性。 ...
  • 摘要:本文詳細介紹了Nuxt3框架中的五個webpack鉤子函數:webpack:configResolved用於在webpack配置解析後讀取和修改配置;webpack:compile在編譯開始前調用,可修改編譯選項;webpack:compiled在編譯完成後調用,可處理編譯結果;webpack... ...
  • ‍ 寫在開頭 點贊 + 收藏 學會 前言: 最近接到了一個需求很有意思,類似於我們經常在逛購物平臺中,選擇一個物品分享給好友,然後好友複製這段文本打開相對應的平臺以後,就可以彈出鏈接上的物品。實現過程也比較有意思,特來分享一下實現思路。 一. 效果預覽 當我在別的界面複製了內 ...
  • 本節課,我們主要講解了在Python類的繼承中子類如何進行初始化、調用父類的屬性和方法,同時講解了模擬串口感測器和主機類的具體實現,並使用xcom串口助手與兩個類進行串口通信使用。 ...
  • # Maven簡介 Maven的本質是一個項目管理工具,將項目開發和管理過程抽象成一個項目對象模型(POM)) 這玩意兒是使用Java開發的,所以採用的就是Java的思想:面向對象 POM (Project Object Model):項目對象模型 Maven的作用: 項目構建:提供標準的、跨平臺的 ...
  • 生成全局唯一 ID 全局唯一 ID 需要滿足以下要求: 唯一性:在分散式環境中,要全局唯一 高可用:在高併發情況下保證可用性 高性能:在高併發情況下生成 ID 的速度必須要快,不能花費太長時間 遞增性:要確保整體遞增的,以便於資料庫創建索引 安全性:ID 的規律性不能太明顯,以免信息泄露 從上面的要 ...
  • 1 註冊中心 1.1 為什麼要用註冊中心 微服務之間會相互調用,假如有兩個服務orderService和userService,orderService會調用userService獲取當前訂單相關的用戶信息,且userService部署了多個實例: 大家思考幾個問題: order-service在發 ...
  • 1、Controller MVC架構中的控制層,在SpringMVC中,由 Handler 組成 負責提供訪問應用程式的行為:處理用戶的請求並調用 Model層 將其轉換為一個模型數據跳向 View層 在Spring MVC中,對於Controller的配置方式有很多種,通常可以通過介面定義或註解定 ...
一周排行
    -Advertisement-
    Play Games
  • 通過WPF的按鈕、文本輸入框實現了一個簡單的SpinBox數字輸入用戶組件並可以通過數據綁定數值和步長。本文中介紹了通過Xaml代碼實現自定義組件的佈局,依賴屬性的定義和使用等知識點。 ...
  • 以前,我看到一個朋友在對一個系統做初始化的時候,通過一組魔幻般的按鍵,調出來一個隱藏的系統設置界面,這個界面在常規的菜單或者工具欄是看不到的,因為它是一個後臺設置的關鍵界面,不公開,同時避免常規用戶的誤操作,它是作為一個超級管理員的入口功能,這個是很不錯的思路。其實Winform做這樣的處理也是很容... ...
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他的程式每次關閉時就會自動崩潰,一直找不到原因讓我幫忙看一下怎麼回事,這位朋友應該是第二次找我了,分析了下 dump 還是挺經典的,拿出來給大家分享一下吧。 二:WinDbg 分析 1. 為什麼會崩潰 找崩潰原因比較簡單,用 !analyze -v 命 ...
  • 在一些報表模塊中,需要我們根據用戶操作的名稱,來動態根據人員姓名,更新報表的簽名圖片,也就是電子手寫簽名效果,本篇隨筆介紹一下使用FastReport報表動態更新人員簽名圖片。 ...
  • 最新內容優先發佈於個人博客:小虎技術分享站,隨後逐步搬運到博客園。 創作不易,如果覺得有用請在Github上為博主點亮一顆小星星吧! 博主開始學習編程於11年前,年少時還只會使用cin 和cout ,給單片機點點燈。那時候,類似async/await 和future/promise 模型的認知還不是 ...
  • 之前在阿裡雲ECS 99元/年的活動實例上搭建了一個測試用的MINIO服務,以前都是直接當基礎設施來使用的,這次準備自己學一下S3相容API相關的對象存儲開發,因此有了這個小工具。目前僅包含上傳功能,後續計劃開發一個類似圖床的對象存儲應用。 ...
  • 目錄簡介快速入門安裝 NuGet 包實體類User資料庫類DbFactory增刪改查InsertSelectUpdateDelete總結 簡介 NPoco 是 PetaPoco 的一個分支,具有一些額外的功能,截至現在 github 星數 839。NPoco 中文資料沒多少,我是被博客園群友推薦的, ...
  • 前言 前面使用 Admin.Core 的代碼生成器生成了通用代碼生成器的基礎模塊 分組,模板,項目,項目模型,項目欄位的基礎功能,本篇繼續完善,實現最核心的模板生成功能,並提供生成預覽及代碼文件壓縮下載 準備 首先清楚幾個模塊的關係,如何使用,簡單畫一個流程圖 前面完成了基礎的模板組,模板管理,項目 ...
  • 假設需要實現一個圖標和文本結合的按鈕 ,普通做法是 直接重寫該按鈕的模板; 如果想作為通用的呢? 兩種做法: 附加屬性 自定義控制項 推薦使用附加屬性的形式 第一種:附加屬性 創建Button的附加屬性 ButtonExtensions 1 public static class ButtonExte ...
  • 在C#中,委托是一種引用類型的數據類型,允許我們封裝方法的引用。通過使用委托,我們可以將方法作為參數傳遞給其他方法,或者將多個方法組合在一起,從而實現更靈活的編程模式。委托類似於函數指針,但提供了類型安全和垃圾回收等現代語言特性。 基本概念 定義委托 定義委托需要指定它所代表的方法的原型,包括返回類 ...