mormot.core.threads--TSynThreadPool
{ ************ 面向伺服器程序的執行緒池 }
TSynThreadPool = class; // 前向宣告TSynThreadPool類
/// 定義了TSynThreadPool所使用的工作執行緒
TSynThreadPoolWorkThread = class(TSynThread)
protected
fOwner: TSynThreadPool; // 執行緒池所有者
fThreadNumber: integer; // 執行緒編號
{$ifndef USE_WINIOCP} // 如果不使用Windows I/O完成埠
fProcessingContext: pointer; // 正在處理的上下文,受fOwner.fSafe.Lock保護
fEvent: TSynEvent; // 同步事件
{$endif USE_WINIOCP}
procedure NotifyThreadStart(Sender: TSynThread); // 通知執行緒開始
procedure DoTask(Context: pointer); // 異常安全地呼叫fOwner.Task()
public
/// 初始化執行緒
constructor Create(Owner: TSynThreadPool); reintroduce;
/// 終結執行緒
destructor Destroy; override;
/// 迴圈等待並執行掛起的任務,透過呼叫fOwner.Task()
procedure Execute; override;
/// 關聯的執行緒池
property Owner: TSynThreadPool
read fOwner;
end;
TSynThreadPoolWorkThreads = array of TSynThreadPoolWorkThread; // 執行緒池工作執行緒陣列型別
/// 一個簡單的執行緒池,用於例如快速處理HTTP/1.0請求
// - 在Windows上透過I/O完成埠實現,或在Linux/POSIX上使用經典的事件驅動方法
TSynThreadPool = class
protected
{$ifndef USE_WINIOCP} // 如果不使用Windows I/O完成埠
fSafe: TOSLightLock; // 使用更穩定的鎖
{$endif USE_WINIOCP}
fWorkThread: TSynThreadPoolWorkThreads; // 工作執行緒陣列
fWorkThreadCount: integer; // 工作執行緒數量
fRunningThreads: integer; // 正在執行的執行緒數量
fExceptionsCount: integer; // 異常計數(未在程式碼中明確使用,但可能用於除錯或監控)
fContentionAbortDelay: integer; // 由於爭用而拒絕連線的延遲時間(毫秒)
fOnThreadTerminate: TOnNotifyThread; // 執行緒終止通知事件
fOnThreadStart: TOnNotifyThread; // 執行緒開始通知事件
fContentionTime: Int64; // 等待佇列可用槽位的總時間(毫秒)
fContentionAbortCount: cardinal; // 由於爭用而中止的任務數
fContentionCount: cardinal; // 等待佇列可用槽位的次數
fName: RawUtf8; // 執行緒池名稱
fTerminated: boolean; // 執行緒池是否已終止
{$ifdef USE_WINIOCP} // 如果使用Windows I/O完成埠
fRequestQueue: THandle; // IOCP有其自己的內部佇列
{$else}
fQueuePendingContext: boolean; // 當所有執行緒都忙時,是否應維護一個內部佇列
fPendingContext: array of pointer; // 掛起的上下文陣列
fPendingContextCount: integer; // 掛起的上下文數量
function GetPendingContextCount: integer; // 獲取掛起上下文數量的函式
function PopPendingContext: pointer; // 從掛起上下文陣列中彈出一個元素的函式
function QueueLength: integer; virtual; // 獲取佇列長度的虛擬函式(可能用於除錯)
{$endif USE_WINIOCP}
/// 在I/O錯誤時結束執行緒
function NeedStopOnIOError: boolean; virtual;
/// 在通知後要執行的程序,這是一個抽象方法,需要被子類實現
procedure Task(aCaller: TSynThreadPoolWorkThread; aContext: pointer); virtual; abstract;
/// 中止任務的程序
procedure TaskAbort(aContext: Pointer); virtual;
public
/// 使用指定的執行緒數初始化執行緒池
// - 抽象的Task()方法將由其中一個執行緒呼叫
// - 一個執行緒池最多可以關聯256個執行緒
// - 在Windows上,可以可選地接受一個之前使用Windows重疊I/O(IOCP)開啟的aOverlapHandle
// - 在POSIX上,如果aQueuePendingContext=true,則將掛起的上下文儲存到內部佇列中,
// 以便在佇列未滿時Push()返回true
{$ifdef USE_WINIOCP}
constructor Create(NumberOfThreads: integer = 32; aOverlapHandle: THandle = INVALID_HANDLE_VALUE; const aName: RawUtf8 = '');
{$else}
constructor Create(NumberOfThreads: integer = 32; aQueuePendingContext: boolean = false; const aName: RawUtf8 = '');
{$endif USE_WINIOCP}
/// 關閉執行緒池,釋放所有關聯的執行緒
destructor Destroy; override;
/// 讓執行緒池處理一個指定的任務(作為指標)
// - 如果沒有空閒執行緒可用,並且Create(aQueuePendingContext=false)被使用,則返回false(呼叫者稍後應重試)
// - 如果在Create中aQueuePendingContext為true,或使用了IOCP,則提供的上下文將被新增到內部列表,並在可能時處理
// - 如果aWaitOnContention預設為false,則在佇列滿時立即返回
// - 設定aWaitOnContention=true以等待最多ContentionAbortDelay毫秒並重試將任務排隊
function Push(aContext: pointer; aWaitOnContention: boolean = false): boolean;
{$ifndef USE_WINIOCP}
/// 在Push()返回false後呼叫,以檢視佇列是否確實已滿
// - 如果QueuePendingContext為false,則返回false
function QueueIsFull: boolean;
/// 如果所有執行緒都忙時,執行緒池是否應維護一個內部佇列
// - 作為Create建構函式的引數提供
property QueuePendingContext: boolean
read fQueuePendingContext;
{$endif USE_WINIOCP}
/// 對此執行緒池中定義的執行緒的低階訪問
property WorkThread: TSynThreadPoolWorkThreads
read fWorkThread;
published
/// 執行緒池中可用的執行緒數
// - 對映Create()引數,即預設為32
property WorkThreadCount: integer
read fWorkThreadCount;
/// 當前在此執行緒池中處理任務的執行緒數
// - 範圍在0..WorkThreadCount之間
property RunningThreads: integer
read fRunningThreads;
/// 由於執行緒池爭用而被拒絕的任務數
// - 如果此數字很高,請考慮設定更高的執行緒數,或分析並調整Task方法
property ContentionAbortCount: cardinal
read fContentionAbortCount;
/// 由於爭用而拒絕連線的延遲時間(毫秒)
// - 預設為5000,即等待IOCP或aQueuePendingContext內部列表中有空間可用5秒
// - 在此延遲期間,不接受新的連線(即不呼叫Accept),以便負載均衡器可以檢測到爭用並切換到池中的另一個例項,
// 或直接客戶端最終可能會拒絕其連線,因此不會開始傳送資料
property ContentionAbortDelay: integer
read fContentionAbortDelay write fContentionAbortDelay;
/// 等待佇列中可用槽位的總時間(毫秒)
// - 爭用不會立即失敗,但會重試直到ContentionAbortDelay
// - 此處的高數值需要對Task方法進行程式碼重構
property ContentionTime: Int64
read fContentionTime;
/// 執行緒池等待佇列中可用槽位的次數
// - 爭用不會立即失敗,但會重試直到ContentionAbortDelay
// - 此處的高數值可能需要增加執行緒數
// - 使用此屬性和ContentionTime來計算平均爭用時間
property ContentionCount: cardinal
read fContentionCount;
{$ifndef USE_WINIOCP}
/// 當前等待分配給執行緒的輸入任務數
property PendingContextCount: integer
read GetPendingContextCount;
{$endif USE_WINIOCP}
end;
{$M-} // 關閉記憶體管理訊息
const
// 允許TSynThreadPoolWorkThread堆疊使用最多256 * 2MB = 512MB的RAM
THREADPOOL_MAXTHREADS = 256;
由於 TSynThreadPool
類是一個高度抽象且依賴於特定實現的類(如它可能使用Windows的I/O完成埠或Linux/POSIX的事件驅動機制),編寫一個完整的例程程式碼可能會相當複雜,並且需要模擬或實際實現這些依賴項。然而,我可以提供一個簡化的示例,該示例展示瞭如何建立 TSynThreadPool
例項、如何向其推送任務,並如何大致實現 Task
方法。
請注意,以下程式碼是一個高度簡化的示例,並不包含所有 TSynThreadPool
類定義中的功能,特別是與Windows I/O完成埠或Linux/POSIX事件驅動機制相關的部分。此外,由於 TSynThreadPool
是一個假設的類(因為它不是Delphi標準庫或廣泛認可的第三方庫的一部分),我將基於您提供的類定義來編寫這個示例。
program TSynThreadPoolDemo;
{$MODE DELPHI}
uses
SysUtils, Classes; // 引入必要的單元
// 假設TSynThreadPool及其依賴項已經在某個單元中定義
// 這裡我們使用一個佔位符單元名YourThreadPoolUnit
uses YourThreadPoolUnit;
// 一個簡單的任務上下文類(僅作為示例)
type
TMyTaskContext = record
Data: Integer;
end;
// TSynThreadPool的Task方法的實現類
type
TMyThreadPool = class(TSynThreadPool)
protected
procedure Task(aCaller: TSynThreadPoolWorkThread; aContext: Pointer); override;
end;
{ TMyThreadPool }
procedure TMyThreadPool.Task(aCaller: TSynThreadPoolWorkThread; aContext: Pointer);
var
Ctx: PMyTaskContext;
begin
Ctx := PMyTaskContext(aContext);
WriteLn('Processing task with data: ', Ctx.Data);
// 在這裡新增處理任務的程式碼
// ...
end;
var
Pool: TMyThreadPool;
Ctx: TMyTaskContext;
I: Integer;
begin
try
// 建立一個執行緒池例項,假設我們想要使用4個工作執行緒
Pool := TMyThreadPool.Create(4);
try
// 模擬向執行緒池推送一些任務
for I := 1 to 10 do
begin
Ctx.Data := I;
if not Pool.Push(@Ctx) then
begin
// 在這個簡化的示例中,我們不會處理Push返回false的情況
// 在實際應用中,您可能需要等待、重試或將任務放入另一個佇列中
WriteLn('Failed to push task to the pool (this should not happen in this simplified example)');
end;
end;
// 在這個簡化的示例中,我們沒有等待所有任務完成
// 在實際應用中,您可能需要等待執行緒池中的所有任務都完成後再繼續
// ...
finally
// 銷燬執行緒池例項,這將釋放所有關聯的資源
Pool.Free;
end;
except
on E: Exception do
WriteLn('An error occurred: ', E.Message);
end;
// 保持控制檯視窗開啟,直到使用者按任意鍵
WriteLn('Press Enter to exit...');
ReadLn;
end.
// 注意:由於TSynThreadPool是一個假設的類,並且上述程式碼沒有實現所有細節(如執行緒池的實際工作執行緒管理、任務佇列等),
// 因此這個示例主要是為了展示如何使用該類(如果它存在的話)的大致結構。
// 在實際應用中,您需要根據TSynThreadPool類的具體實現來調整此程式碼。
重要說明:
- 佔位符單元:在上述程式碼中,我使用了
YourThreadPoolUnit
作為包含TSynThreadPool
類定義的佔位符單元名。在實際應用中,您需要將其替換為包含該類定義的實際單元名。 - 任務上下文:我定義了一個簡單的
TMyTaskContext
記錄型別來作為任務的上下文。在實際應用中,您可能需要根據需要定義更復雜的上下文型別。 - 錯誤處理:在
Push
方法返回false
的情況下,上述程式碼僅列印了一條訊息,並沒有採取任何恢復措施。在實際應用中,您可能需要實現更復雜的錯誤處理邏輯(如重試、等待或將任務放入另一個佇列中)。 - 等待任務完成:上述程式碼沒有等待執行緒池中的所有任務都完成。在實際應用中,您可能需要實現某種形式的等待機制(例如,使用同步事件或計數器)來確保所有任務都已完成後再繼續執行後續程式碼。
- 執行緒池實現:由於
TSynThreadPool
是一個假設的類,並且其實現細節(如工作執行緒的管理、任務佇列的實現等)並未在您的類定義中給出,因此上述程式碼僅提供了一個大致的框架。在實際應用中,您需要根據TSynThreadPool
類的具體實現來調整此程式碼。