mormot.core.threads--TSynBackgroundThreadMethod

海利鸟發表於2024-07-08

mormot.core.threads--TSynBackgroundThread

在mORMot 2框架中,TSynBackgroundThreadEventTSynBackgroundThreadMethodTSynBackgroundThreadProcedureTSynBackgroundThreadProcessTSynBackgroundTimer這幾個類雖然都涉及到後臺執行緒的執行,但它們各自有不同的用途和設計目標。以下是對這些類之間差異的概述:

  1. TSynBackgroundThreadEvent

    • 這個類可能設計用於執行與事件相關的回撥函式。它可能封裝了一個事件處理機制,允許您在後臺執行緒中響應特定的事件。
    • 回撥函式的簽名可能包括一個事件傳送者(Sender)引數,以及可能的其他引數,用於傳遞事件相關的資料。
    • 由於名稱中包含“Event”,它可能特別適用於事件驅動的場景。
  2. TSynBackgroundThreadMethod

    • 這個類設計用於執行標準的 TThreadMethod型別的回撥,即無引數且返回 Void的過程。
    • 它提供了一種在後臺執行緒中執行這些回撥的簡便方式,使得與Delphi標準執行緒庫相容的程式碼可以輕鬆地與mORMot 2框架整合。
    • 由於 TThreadMethod是Delphi中用於執行緒方法的標準型別,因此這個類可能特別適用於需要這種相容性的場景。
  3. TSynBackgroundThreadProcedure

    • 這個類可能設計用於執行簡單的無引數過程(procedure)回撥,而不是一個物件方法或 TThreadMethod
    • 它與 TSynBackgroundThreadMethod類似,但可能更加專注於那些不需要傳遞額外引數的過程。
    • 使用這個類可以使程式碼更加簡潔,特別是當回撥不需要訪問任何外部狀態或引數時。
  4. TSynBackgroundThreadProcess

    • 這個類可能是一個更通用的後臺執行緒處理類,它可能提供了執行週期性任務或長時間執行任務的能力。
    • 它可能封裝了執行緒建立、啟動、暫停、恢復和終止的邏輯,以及任務排程的機制。
    • 與其他幾個類相比,TSynBackgroundThreadProcess可能更加靈活和強大,適用於需要複雜任務排程的場景。
  5. TSynBackgroundTimer

    • 這個類專門設計用於在後臺執行緒中執行定時任務。
    • 它可能內部維護了一個或多個定時器,允許您安排任務在指定的時間間隔後執行。
    • TSynBackgroundTimer可能還提供了向定時任務傳遞訊息或引數的能力,以及查詢任務狀態和執行結果的方法。
    • 它特別適用於需要定時執行任務的場景,如心跳檢測、定時輪詢等。

上述描述是基於對類名的一般性推斷和假設。實際上,mORMot 2框架中的這些類的具體實現和用途可能有所不同。因此,如果您正在使用mORMot 2框架,並且想要了解這些類的確切差異和用法,最好的做法是查閱mORMot 2的官方文件或原始碼。這將為您提供關於這些類的詳細資訊和示例程式碼,幫助您更好地理解和使用它們。

TSynBackgroundThreadMethodAbstrac 定義

/// TSynBackgroundThreadAbstract 程序的狀態機狀態
TSynBackgroundThreadProcessStep = (
    flagIdle,           // 空閒狀態
    flagStarted,        // 已啟動狀態
    flagFinished,       // 已完成狀態
    flagDestroying);    // 正在銷燬狀態

/// TSynBackgroundThreadAbstract程序的狀態機狀態集合
TSynBackgroundThreadProcessSteps = set of TSynBackgroundThreadProcessStep;


/// 抽象TThread類,能夠在其自己的執行內容中執行一個方法
// - 典型用途是作為處理資料或遠端訪問的後臺執行緒,
// 同時UI透過迴圈執行OnIdle事件保持響應:
// 例如,檢視mormot.rest.client.pas單元中TRestClientUri.OnIdle是如何處理此事件的
// - 您不應直接使用此類,而應繼承它並重寫Process方法,
// 或者使用TSynBackgroundThreadEvent/TSynBackgroundThreadMethod,
// 並提供一個更加方便的回撥函式
TSynBackgroundThreadMethodAbstract = class(TSynBackgroundThreadAbstract)
protected
    fPendingProcessLock: TLightLock;     // 對fPendingProcessFlag的原子訪問
    fCallerEvent: TSynEvent;             // 呼叫者事件
    fParam: pointer;                     // 引數指標
    fCallerThreadID: TThreadID;          // 呼叫者執行緒ID
    fBackgroundException: Exception;     // 後臺執行緒異常
    fOnIdle: TOnIdleSynBackgroundThread; // 空閒時回撥事件
    fOnBeforeProcess: TOnNotifyThread;   // 處理前通知回撥
    fOnAfterProcess: TOnNotifyThread;    // 處理後通知回撥
    fPendingProcessFlag: TSynBackgroundThreadProcessStep;        // 待處理標誌
    procedure ExecuteLoop; override;     // 重寫執行迴圈
    function OnIdleProcessNotify(var start: Int64): Int64;       // 空閒處理通知,返回已用毫秒數
    function GetOnIdleBackgroundThreadActive: boolean;           // 獲取OnIdle事件是否啟用
    function GetPendingProcess: TSynBackgroundThreadProcessStep; // 獲取待處理狀態
    procedure SetPendingProcess(State: TSynBackgroundThreadProcessStep); // 設定待處理狀態
    // 如果獲取成功則返回flagIdle,如果已終止則返回flagDestroying
    function AcquireThread: TSynBackgroundThreadProcessStep;
    procedure WaitForFinished(start: Int64; const onmainthreadidle: TNotifyEvent); // 等待完成
  
    /// 當fProcessParams<>nil且fEvent被通知時,由Execute方法呼叫
    procedure Process; virtual; abstract;                           // 抽象方法,需在子類中實現
public
    /// 初始化執行緒
    // - 如果aOnIdle未設定(即等於nil),它將簡單地等待後臺程序完成,直到RunAndWait()返回
    // - 您可以定義一些回撥來巢狀執行緒執行,例如,分配給TRestServer.BeginCurrentThread/EndCurrentThread
    constructor Create(const aOnIdle: TOnIdleSynBackgroundThread;
      const aThreadName: RawUtf8; 
      const OnBeforeExecute: TOnNotifyThread = nil;
      const OnAfterExecute: TOnNotifyThread = nil); reintroduce;
  
    /// 銷燬執行緒
    destructor Destroy; override;
  
    /// 在後臺執行緒中非同步啟動Process抽象方法
    // - 等待程序完成,同時呼叫OnIdle()回撥
    // - 在後臺執行緒中引發的任何異常都將在呼叫者執行緒中轉換
    // - 如果self未設定,或者從當前正在處理的同一執行緒呼叫(以避免OnIdle()回撥中的競態條件),則返回false
    // - 當後臺程序完成時返回true
    // - OpaqueParam將用於指定後臺程序執行緒安全的內容
    // - 此方法是執行緒安全的,即它將等待另一個執行緒已經啟動的任何程序:
    // 您可以從任何執行緒呼叫此方法,即使其主要用途是從主UI執行緒呼叫
    function RunAndWait(OpaqueParam: pointer): boolean;
  
    /// 設定一個回撥事件,在遠端阻塞過程期間迴圈執行,
    // 例如,在長時間請求期間重新整理UI
    // - 您可以為此屬性分配一個回撥,例如呼叫Application.ProcessMessages,
    // 以在後臺執行緒中執行遠端請求,但讓UI仍然具有響應性:
    // mORMotUILogin.pas中的TLoginForm.OnIdleProcess和OnIdleProcessForm方法將符合此屬性的預期
    // - 如果OnIdle未設定(即等於nil),它將簡單地等待後臺程序完成,直到RunAndWait()返回
    property OnIdle: TOnIdleSynBackgroundThread   read fOnIdle write fOnIdle;
  
    /// 如果後臺執行緒處於活動狀態,並且在處理過程中呼叫了OnIdle事件,則為TRUE
    // - 例如,用於確保沒有來自使用者介面訊息的重新進入
    property OnIdleBackgroundThreadActive: boolean read GetOnIdleBackgroundThreadActive;
  
    /// 在Execute中每次處理之前觸發的可選回撥事件
    property OnBeforeProcess: TOnNotifyThread   read fOnBeforeProcess write fOnBeforeProcess;
  
    /// 在Execute中每次處理之後觸發的可選回撥事件
    property OnAfterProcess: TOnNotifyThread    read fOnAfterProcess write fOnAfterProcess;
end;

TSynBackgroundThreadEvent,TSynBackgroundThreadMethod,TSynBackgroundThreadProcedure,TSynBackgroundThreadProcess

TSynBackgroundThreadEvent 定義

/// 由 TSynBackgroundThreadEvent 呼叫的後臺程序方法
// - 當執行Process虛擬方法時,將提供RunAndWait()方法中指定的OpaqueParam引數
TOnProcessSynBackgroundThread = procedure(Sender: TSynBackgroundThreadEvent;
    ProcessOpaqueParam: pointer) of object;

/// 允許後臺執行緒處理方法回撥
TSynBackgroundThreadEvent = class(TSynBackgroundThreadMethodAbstract)
protected
    fOnProcess: TOnProcessSynBackgroundThread; // 回撥事件
  
    /// 僅呼叫OnProcess處理程式
    procedure Process; override; // 重寫Process方法

public
    /// 初始化執行緒
    // - 如果aOnIdle未設定(即等於nil),它將簡單地等待後臺程序完成,直到RunAndWait()返回
    constructor Create(const aOnProcess: TOnProcessSynBackgroundThread;
      const aOnIdle: TOnIdleSynBackgroundThread;
      const aThreadName: RawUtf8); reintroduce; // 重引入建構函式
  
    /// 提供一個方法處理程式,在後臺執行緒中執行
    // - 由RunAndWait()方法觸發 - 該方法將等待直到完成
    // - 這裡將提供RunAndWait()方法中指定的OpaqueParam
    property OnProcess: TOnProcessSynBackgroundThread
      read fOnProcess write fOnProcess; // OnProcess屬性,用於訪問fOnProcess欄位
end;

TSynBackgroundThreadMethod 定義

/// 允許後臺執行緒處理可變的TThreadMethod回撥
TSynBackgroundThreadMethod = class(TSynBackgroundThreadMethodAbstract)
protected
    /// 僅呼叫RunAndWait()方法中提供的TThreadMethod
    procedure Process; override; // 重寫Process方法
public
    /// 執行一次提供的TThreadMethod回撥
    // - 使用此方法,而不是繼承的RunAndWait()
    procedure RunAndWait(Method: TThreadMethod); reintroduce; // 重引入RunAndWait方法
end;

TSynBackgroundThreadProcedure 定義

/// 由 TSynBackgroundThreadProcedure 呼叫的後臺程序過程
// - 當執行Process虛擬方法時,將提供RunAndWait()方法中指定的OpaqueParam引數
TOnProcessSynBackgroundThreadProc = procedure(ProcessOpaqueParam: pointer);

/// 允許後臺執行緒處理過程回撥
TSynBackgroundThreadProcedure = class(TSynBackgroundThreadMethodAbstract)
protected
    fOnProcess: TOnProcessSynBackgroundThreadProc; // 回撥過程
    /// 僅呼叫OnProcess處理程式
    procedure Process; override; // 重寫Process方法
public
    /// 初始化執行緒
    // - 如果aOnIdle未設定(即等於nil),它將簡單地等待後臺程序完成,直到RunAndWait()返回
    constructor Create(aOnProcess       : TOnProcessSynBackgroundThreadProc;
                       const aOnIdle    : TOnIdleSynBackgroundThread;
                       const aThreadName: RawUtf8); reintroduce; // 重引入建構函式
  
    /// 提供一個過程處理程式,在後臺執行緒中執行
    // - 由RunAndWait()方法觸發 - 該方法將等待直到完成
    // - 這裡將提供RunAndWait()方法中指定的OpaqueParam
    property OnProcess: TOnProcessSynBackgroundThreadProc 
      read fOnProcess write fOnProcess; // OnProcess屬性,用於訪問fOnProcess欄位
end;

TSynBackgroundThreadProcess 定義

type
  // TSynBackgroundThreadProcess 類宣告(稍後定義)
  TSynBackgroundThreadProcess = class;

  /// 由 TSynBackgroundThreadProcess 定期執行的事件回撥
  TOnSynBackgroundThreadProcess = procedure(Sender: TSynBackgroundThreadProcess) of object;

  /// 能夠以給定週期執行方法的 TThread 類
  TSynBackgroundThreadProcess = class(TSynBackgroundThreadAbstract)
  protected
    fOnProcess: TOnSynBackgroundThreadProcess; // 定期執行的方法回撥
    fOnException: TNotifyEvent; // 當 OnProcess 引發異常時執行的事件回撥
    fOnProcessMS: cardinal; // 定期執行任務的時間間隔(毫秒)
    fStats: TSynMonitor; // 處理統計資訊
    procedure ExecuteLoop; override; // 重寫執行迴圈
  public
    /// 初始化執行緒以進行週期性任務處理
    // - 當 ProcessEvent.SetEvent 被呼叫或自上次處理以來過去了 aOnProcessMS 毫秒時,將呼叫 aOnProcess
    // - 如果 aOnProcessMS 為 0,則等待直到 ProcessEvent.SetEvent 被呼叫
    // - 您可以定義一些回撥來巢狀執行緒執行,例如,分配給 TRestServer.BeginCurrentThread/EndCurrentThread
    constructor Create(
        const aThreadName     : RawUtf8;
        const aOnProcess      : TOnSynBackgroundThreadProcess;
        aOnProcessMS          : cardinal; 
        const aOnBeforeExecute: TOnNotifyThread = nil;
        const aOnAfterExecute : TOnNotifyThread = nil;
        aStats                : TSynMonitorClass = nil;
        CreateSuspended       : boolean = false); reintroduce; virtual;
  
    /// 終結執行緒並等待其結束
    destructor Destroy; override;
  
    /// 訪問週期性任務的實現事件
    property OnProcess: TOnSynBackgroundThreadProcess
      read fOnProcess;
  
    /// 當 OnProcess 引發異常時執行的事件回撥
    // - 提供的 Sender 引數是引發的異常例項
    property OnException: TNotifyEvent
      read fOnException write fOnException;
  
  published
  
    /// 訪問週期性任務處理的延遲時間(毫秒)
    property OnProcessMS: cardinal  read fOnProcessMS write fOnProcessMS;
  
    /// 處理統計資訊
    // - 如果在類建構函式中 aStats 為 nil,則可能為 nil
    property Stats: TSynMonitor   read fStats;
  end;

TSynBackgroundThreadEvent 例程程式碼

uses
  SysUtils, Classes, // 引入必要的單元
  mormot.core.threads;

type
  TMyProcessEvent = procedure(Sender: TObject; Param: Pointer) of object;

var
  MyProcessEvent: TMyProcessEvent;

procedure MyBackgroundProcess(Sender: TObject; ProcessOpaqueParam: Pointer);
begin
  // 這裡是後臺處理的程式碼
  WriteLn('Background process running with param: ', Pointer(ProcessOpaqueParam)^);
  // 假設我們傳遞了一個Integer指標作為OpaqueParam
  Integer(ProcessOpaqueParam)^ := Integer(ProcessOpaqueParam)^ + 1;
end;

procedure InitializeAndRunBackgroundThreadEvent;
var
  BGThread: TSynBackgroundThreadEvent;
  Param: PInteger;
begin
  New(Param);
  Param^ := 10; // 初始化引數

  MyProcessEvent := MyBackgroundProcess;

  // 建立並啟動後臺執行緒事件
  BGThread := TSynBackgroundThreadEvent.Create(
    MyProcessEvent, // 傳遞我們的處理過程
    nil, // OnIdle回撥,這裡不使用
    'MyBackgroundThreadEventDemo'
  );
  try
    // 執行後臺執行緒並等待完成
    BGThread.RunAndWait(Param);
    WriteLn('Background process finished. New param value: ', Param^);
  finally
    BGThread.Free; // 銷燬執行緒物件
    Dispose(Param); // 釋放引數記憶體
  end;
end;

// 在程式的某個地方呼叫InitializeAndRunBackgroundThreadEvent

TSynBackgroundThreadMethod 例程程式碼

uses
  SysUtils, Classes,
  mormot.core.threads;

procedure MyThreadMethod(Thread: TThread);
begin
  // 這裡是執行緒方法的程式碼
  WriteLn('Thread method running in background');
  // 模擬一些耗時操作
  Sleep(1000);
end;

procedure RunBackgroundThreadMethod;
var
  BGThread: TSynBackgroundThreadMethod;
begin
  // 建立後臺執行緒方法物件
  BGThread := TSynBackgroundThreadMethod.Create(nil, nil, 'MyBackgroundThreadMethodDemo');
  try
    // 注意:這裡我們重寫了RunAndWait方法,所以直接傳遞TThreadMethod
    BGThread.RunAndWait(MyThreadMethod);
  finally
    BGThread.Free; // 銷燬執行緒物件
  end;
end;

// 在程式的某個地方呼叫RunBackgroundThreadMethod

注意:上面的 TSynBackgroundThreadMethod示例中,我假設了 RunAndWait方法被重寫以接受 TThreadMethod作為引數,但根據原始定義,這並不是直接支援的。通常,您會在 Process方法內部呼叫傳入的 TThreadMethod。為了保持示例簡單,我展示瞭如何可能使用它,但在實際應用中,您可能需要在 Process方法內部處理這一點。

TSynBackgroundThreadProcedure 例程程式碼

uses
  SysUtils, Classes,
  mormot.core.threads;

procedure MyBackgroundProcedure(ProcessOpaqueParam: Pointer);
begin
  // 這裡是後臺過程的程式碼
  WriteLn('Background procedure running with param: ', Pointer(ProcessOpaqueParam)^);
  // 假設我們傳遞了一個字串指標作為OpaqueParam
  WriteLn('String param: ', PString(ProcessOpaqueParam)^);
end;

procedure InitializeAndRunBackgroundThreadProcedure;
var
  BGThread: TSynBackgroundThreadProcedure;
  Param: PString;
begin
  New(Param);
  Param^ := 'Hello, Background!';

  // 建立並啟動後臺執行緒過程
  BGThread := TSynBackgroundThreadProcedure.Create(
    MyBackgroundProcedure, // 傳遞我們的處理過程
    nil, // OnIdle回撥,這裡不使用
    'MyBackgroundThreadProcedureDemo'
  );
  try
    // 執行後臺執行緒並等待完成
    BGThread.RunAndWait(Param);
  finally
    BGThread.Free; // 銷燬執行緒物件
    Dispose(Param); // 釋放引數記憶體
  end;
end;

// 在程式的某個地方呼叫InitializeAndRunBackgroundThreadProcedure

TSynBackgroundThreadProcess 例程程式碼

uses
  SysUtils, Classes, // 引入SysUtils和Classes單元以使用WriteLn和TThread等
  mormot.core.threads;

procedure MyProcessMethod(Sender: TSynBackgroundThreadProcess);
begin
  WriteLn('Process method called in background thread.');
  // 在這裡執行您的後臺處理邏輯
end;

var
  BGThread: TSynBackgroundThreadProcess;

begin
  try
    // 建立TSynBackgroundThreadProcess例項
    BGThread := TSynBackgroundThreadProcess.Create(
      'MyBackgroundThread', // 執行緒名稱
      MyProcessMethod, // 週期性執行的方法
      1000, // 週期時間,單位為毫秒
      nil, // OnBeforeExecute回撥,這裡不使用
      nil  // OnAfterExecute回撥,這裡不使用
      // aStats和其他引數根據需要進行設定
    );
    try
      // 啟動執行緒(注意:在TSynBackgroundThreadProcess的建構函式中,
      // 如果CreateSuspended引數為false,則執行緒將自動啟動)
      // 在這個例子中,我們假設CreateSuspended預設為false

      // 等待一段時間以觀察後臺執行緒的行為
      // 注意:在實際應用中,您可能不需要這樣做,因為主執行緒可能會繼續執行其他任務
      Sleep(5000); // 等待5秒

    finally
      // 銷燬執行緒物件(注意:在解構函式中,執行緒將嘗試優雅地終止)
      BGThread.Free;
      // 等待執行緒真正結束(可選,但在這個例子中我們依賴解構函式的行為)
    end;
  except
    on E: Exception do
      WriteLn('Error: ' + E.Message);
  end;
  WriteLn('Program ended.');
end.

注意:在上面的示例中,我假設 TSynBackgroundThreadProcess的建構函式有一個 CreateSuspended引數(這在標準的 TThread建構函式中是存在的),但根據您提供的類定義,這個引數實際上並沒有在 TSynBackgroundThreadProcess的建構函式中明確列出。如果 TSynBackgroundThreadProcess是自動啟動執行緒的,那麼您可能不需要顯式呼叫任何啟動方法。

TSynBackgroundTimer 例程程式碼

uses
  SysUtils, // 引入SysUtils單元以使用WriteLn
  mormot.core.threads;

procedure MyTimerProcess(Sender: TSynBackgroundTimer; const Msg: RawUtf8);
begin
  WriteLn('Timer process called in background thread. Message: ' + Msg);
  // 在這裡執行您的定時任務邏輯
end;

var
  Timer: TSynBackgroundTimer;

begin
  try
    // 建立TSynBackgroundTimer例項
    Timer := TSynBackgroundTimer.Create(
      'MyBackgroundTimer' // 執行緒名稱
      // 其他引數根據需要進行設定,這裡省略了OnBeforeExecute、OnAfterExecute、aStats和aLogClass
    );
    try
      // 啟用一個週期性任務,每2秒執行一次
      Timer.Enable(@MyTimerProcess, 2);

      // 向任務佇列新增訊息,並請求立即執行(儘管在這個上下文中,立即執行可能不會立即發生)
      Timer.EnQueue(@MyTimerProcess, 'Hello from background timer!', true);

      // 等待一段時間以觀察後臺定時器的行為
      // 注意:在實際應用中,您可能不需要這樣做,因為主執行緒可能會繼續執行其他任務
      Sleep(10000); // 等待10秒

      // 禁用週期性任務(在這個示例中我們不會禁用它,但展示瞭如何禁用)
      // Timer.Disable(@MyTimerProcess);

    finally
      // 銷燬TSynBackgroundTimer例項(注意:在實際應用中,您可能希望等待所有後臺任務完成後再銷燬定時器)
      // 但在這個簡單示例中,我們立即銷燬它
      Timer.Free;
      // 由於我們立即銷燬了定時器,並且主執行緒繼續執行(儘管在這個示例中被Sleep阻塞了),
      // 因此後臺執行緒可能沒有機會執行更多的回撥。
      // 在實際應用中,您應該確保在銷燬定時器之前給後臺執行緒足夠的時間來完成其工作。
    end;
  except
    on E: Exception do
      WriteLn('Error: ' + E.Message);
  end;
  WriteLn('Program ended.');
end.

在上面的 TSynBackgroundTimer示例中,我展示瞭如何建立定時器、啟用週期性任務、向任務佇列新增訊息,並等待一段時間以觀察定時器的行為。請注意,由於我們呼叫了 Sleep並且立即銷燬了定時器,因此後臺執行緒可能沒有機會執行更多的回撥。在實際應用中,您應該確保在銷燬定時器之前給後臺執行緒足夠的時間來完成其工作,或者呼叫 Timer.WaitUntilNotProcessing(如果該類提供了這樣的方法)來等待所有後臺任務完成。然而,根據提供的類定義,TSynBackgroundTimer並沒有直接提供 WaitUntilNotProcessing方法,所以您可能需要實現自己的同步機制來達到這個目的。