{
//單元:ThreadPoolUint
//說明:線程池
//
//Rev. 開發日期 開發者 EMail
//Ver.1.0.0 2011/05/05 孫玉良 [email protected]
}
unit ThreadPoolUint;
// 定義多線程共享讀獨佔寫條件編譯
{$DEFINE MULTI_THREAD_WRITE-READ}
interface
uses System.Classes, System.SysUtils, System.Math, System.Generics.Collections,
Vcl.Forms;
type
// 要執行任務的記錄
TaskRec = record
isSynchronize: Boolean; // 是否需要同步執行
TaskProc: TThreadProcedure; // 要執行任務的方法
end;
// 執行具體任務線程
TExecuteThread = class(TThread)
private
FProc: TThreadProcedure; // 要執行的任務方法
FIsCanTask: Boolean; // 是否可以執行任務
FIsSynchronize: Boolean; // 是否用同步執行
procedure showThreadID; // 顯示線程編號(測試使用)
protected
procedure Execute; override;
public
constructor Create(CreateSuspended: Boolean); overload;
public
procedure StartTask(task: TaskRec); // 執行任務
end;
// 線程池類(單例模式的類,做爲全局使用的類)
ThreadPool = class(TObject)
private
{$IFDEF MULTI_THREAD_WRITE-READ}
FMREWSync: TMREWSync; // 共享讀獨佔寫變量
{$ENDIF}
FTaskQueue: TQueue<TaskRec>; // 要執行任務隊列
FTaskThreadList: TList<TExecuteThread>; // 執行任務線程List
FThreadMin: Integer; // 最小線程數量
FThreadMax: Integer; // 最大線程數量
// 共享讀獨佔寫方法
procedure BeginWrite; // 獨佔寫開始
procedure EndWrite; // 獨佔寫結束
procedure BeginRead; // 共享讀開始
procedure EndRead; // 共享讀結束
procedure StopTaskAndFree; // 停止執行任務並釋放相關資源
protected
constructor CreateInstance(const minCount: Integer = 5;
const maxCount: Integer = 20);
class function AccessInstance(Request: Integer; const minCount: Integer = 5;
const maxCount: Integer = 20): ThreadPool;
public
constructor Create; // 構造函數
destructor destroy; override; // 析構函數
class function Instance(const minCount: Integer = 5;
const maxCount: Integer = 20): ThreadPool; // 實例化函數,客戶端調用此函數
class procedure ReleaseInstance; // 釋放資源函數,客戶端調用此函數
procedure AddTask(task: TaskRec); // 添加要執行的任務
function IsHaveTask: Boolean; // 是否有要執行的任務
procedure ExecuteTask; // 執行任務
function DoNextTask(executeThread: TExecuteThread): Boolean; // 執行下一任務
function IsSuspend(executeThread: TExecuteThread): Boolean; // 掛起線程
function GetPoolState: string; // 得到線程池狀態
end;
implementation
{$J+}
{ MainUnit是爲了測試引入的窗體單元,實際使用時候刪除此單元和相關代碼 }
uses MainUnit;
// -----------------------------------------------------------------------------
// 構造函數
constructor ThreadPool.Create;
begin
inherited Create;
raise Exception.CreateFmt('Utils類只能通過Instance方法來創建和訪問%s的實例!', [ClassName]);
end;
// 創建實例方法
constructor ThreadPool.CreateInstance(const minCount: Integer = 5;
const maxCount: Integer = 20);
var
i: Integer;
begin
inherited Create;
// 需要在構造函數中初始化數據全部在此初始化
{$IFDEF MULTI_THREAD_WRITE-READ}
// 創建多線程共享讀獨佔寫變量
Self.FMREWSync := TMREWSync.Create;
{$ENDIF}
Self.FTaskQueue := TQueue<TaskRec>.Create; // 實例化要執行的任務隊列
Self.FTaskThreadList := TList<TExecuteThread>.Create; // 實例化執行任務線程List
Self.FThreadMin := minCount; // 最小線程數量
Self.FThreadMax := maxCount; // 最大線程數量
// 創建最小數量的線程
for i := 0 to minCount - 1 do
begin
// 把線程添加到線程List中
Self.FTaskThreadList.Add(TExecuteThread.Create(true));
end;
end;
// 析構函數
destructor ThreadPool.destroy;
begin
// 需要析構前完成操作全部在此完成
Self.StopTaskAndFree; // 釋放線程池資源
{$IFDEF MULTI_THREAD_WRITE-READ}
// 釋放多線程共享讀獨佔寫變量
Self.FMREWSync.Free;
{$ENDIF}
if AccessInstance(0) = Self then
begin
AccessInstance(2);
end;
inherited destroy;
end;
class function ThreadPool.AccessInstance(Request: Integer;
const minCount: Integer = 5; const maxCount: Integer = 20): ThreadPool;
const
FInstance: ThreadPool = nil;
begin
{
AccessInstance(0):不作任何處理,供釋放實例對象時使用。
AccessInstance(1):存在該實例時直接使用,不存在時則創建該實例。
AccessInstance(2):返回一個空指針,用於重新設置實例。
}
case Request of
0:
;
1:
if not Assigned(FInstance) then
begin
FInstance := CreateInstance(minCount, maxCount);
end;
2:
FInstance := nil;
else
raise Exception.CreateFmt(' %d 是AccessInstance()中的非法調用參數。', [Request]);
end;
Result := FInstance;
end;
// 得到類實例
class function ThreadPool.Instance(const minCount: Integer = 5;
const maxCount: Integer = 20): ThreadPool;
begin
// 返回實例
Result := AccessInstance(1, minCount, maxCount);
end;
// 釋放資源
class procedure ThreadPool.ReleaseInstance;
begin
AccessInstance(0).Free;
end;
{ ---- 類函數結束 ---- }
procedure ThreadPool.StopTaskAndFree;
var
whileCount: Integer; // while循環計數變量
taskThread: TExecuteThread;
begin
// 1,釋放線程List
try
Self.BeginWrite;
whileCount := 0; // while循環計數默認值爲0
while whileCount < Self.FTaskThreadList.count do
begin
taskThread := Self.FTaskThreadList.Items[whileCount]; // 得到工作線程
Self.FTaskThreadList.Delete(whileCount); // 從線程列表中刪除線程
taskThread.Terminate; // 終止線程
Inc(whileCount); // while循環計數遞增
end;
finally
Self.EndWrite;
Self.FTaskThreadList.Free; // 釋放線程List
end;
// 2,釋放任務隊列
Self.FTaskQueue.Clear;
Self.FTaskQueue.Free;
end;
// 獨佔寫開始
procedure ThreadPool.BeginWrite;
begin
{$IFDEF MULTI_THREAD_WRITE-READ}
Self.FMREWSync.BeginWrite;
{$ENDIF}
end;
// 獨佔寫結束
procedure ThreadPool.EndWrite;
begin
{$IFDEF MULTI_THREAD_WRITE-READ}
Self.FMREWSync.EndWrite;
{$ENDIF}
end;
// 共享讀開始
procedure ThreadPool.BeginRead;
begin
{$IFDEF MULTI_THREAD_WRITE-READ}
Self.FMREWSync.BeginRead;
{$ENDIF}
end;
// 共享讀結束
procedure ThreadPool.EndRead;
begin
{$IFDEF MULTI_THREAD_WRITE-READ}
Self.FMREWSync.EndRead;
{$ENDIF}
end;
// 給線程池添加任務
procedure ThreadPool.AddTask(task: TaskRec);
begin
// 添加任務到線程池中
try
Self.BeginWrite;
Self.FTaskQueue.Enqueue(task); // 把要執行任務加入任務隊列
finally
Self.EndWrite;
end;
end;
// 是否有要執行的任務
function ThreadPool.IsHaveTask: Boolean;
var
temp: Boolean;
begin
temp := false;
try
Self.BeginRead;
// 判斷有要執行的任務
if Self.FTaskQueue.count > 0 then
begin
temp := true;
end;
finally
Self.EndRead;
end;
Result := temp;
end;
// 執行任務
procedure ThreadPool.ExecuteTask;
var
whileCount: Integer; // while循環計數變量
isCanCreateThread: Boolean; // 是否可以創建新線程
curThread: TExecuteThread;
begin
// 在主界面memo中顯示信息
Form1.log('開始執行任務'); // 測試使用,正式使用刪除
if Self.IsHaveTask then
begin
// 1,判斷是否有可以執行任務線程,如果有直接讓線程執行
try
Self.BeginRead;
whileCount := 0; // while循環計數變量默認值爲0
while whileCount < Self.FTaskThreadList.count do
begin
// 判斷當前線程爲掛起狀態
if Self.FTaskThreadList.Items[whileCount].Suspended then
begin
Self.FTaskThreadList.Items[whileCount].Resume; // 喚醒掛起線程
end;
Inc(whileCount); // while循環計數遞增
end;
finally
Self.EndRead;
// 判斷有要執行的任務
if Self.IsHaveTask then
begin
// 是否可以創建新線程默認值爲false
isCanCreateThread := false;
try
Self.BeginRead;
// 判斷當前線程總數小於最大線程數量
if Self.FTaskThreadList.count < Self.FThreadMax then
begin
isCanCreateThread := true;
/// /是否可以創建新線程爲true
end;
finally
Self.EndRead;
// 判斷可以創建新線程
if isCanCreateThread then
begin
while Self.FTaskThreadList.count < Self.FThreadMax do
begin
// 創建新線程
curThread := TExecuteThread.Create(true);
try
Self.BeginWrite;
// 把新線程加入線程List
Self.FTaskThreadList.Add(curThread);
finally
Self.EndWrite;
end;
curThread.Resume;
end;
end;
end;
end;
end;
end;
end;
// 執行下一任務
function ThreadPool.DoNextTask(executeThread: TExecuteThread): Boolean;
var
isDoNextTask: Boolean; // 是否執行下一任務
nextTaskRec: TaskRec; // 下一任務結構
temp: Boolean;
begin
temp := false; // 返回布爾值默認值爲false
try
isDoNextTask := false; // 是否執行下一任務默認值爲false
Self.BeginWrite;
// 判斷有要執行的任務
if Self.FTaskQueue.count > 0 then
begin
nextTaskRec := Self.FTaskQueue.Dequeue;
isDoNextTask := true; // 是否執行任務爲true
temp := true; // 返回布爾值爲true
end;
finally
Self.EndWrite;
// 判斷執行下一任務
if isDoNextTask then
begin
executeThread.StartTask(nextTaskRec); // 執行任務
end;
end;
Result := temp;
end;
// 判斷線程是否需要掛起
function ThreadPool.IsSuspend(executeThread: TExecuteThread): Boolean;
var
temp: Boolean;
isRemove: Boolean;
begin
temp := false;
try
Self.BeginRead;
isRemove := false; // 是否從線程List中刪除當前線程默認值爲false
// 判斷線程數量是否大於最小線程數量
if Self.FTaskThreadList.count > Self.FThreadMin then
begin
isRemove := true; // 是否從線程List中刪除當前線程爲true
end
else
begin
temp := true; // 是否掛起爲true
end;
finally
Self.EndRead;
// 判斷從線程List中刪除當前線程
if isRemove then
begin
try
Self.BeginWrite;
// 從線程List中刪除當前線程
Self.FTaskThreadList.Remove(executeThread);
finally
Self.EndWrite;
end;
end;
end;
Result := temp;
end;
// 得到線程池狀態
function ThreadPool.GetPoolState: string;
var
temp: string; // 返回值變量
i: Integer; // 循環計數變量
curThread: TExecuteThread;
begin
temp := '線程狀態:' + #13#10;;
temp := temp + '最小線程數:' + inttostr(Self.FThreadMin) + #13#10;
temp := temp + '最大線程數:' + inttostr(Self.FThreadMax) + #13#10;
try
Self.BeginRead;
temp := temp + '線程總數:' + inttostr(Self.FTaskThreadList.count) + #13#10;
temp := temp + #13#10;
temp := temp + '線程詳細信息:' + #13#10;
temp := temp + #13#10;
for i := 0 to Self.FTaskThreadList.count - 1 do
begin
curThread := Self.FTaskThreadList.Items[i];
temp := temp + '線程-' + inttostr(i + 1) + #13#10;
temp := temp + '線程編號:' + inttostr(curThread.ThreadID) + #13#10;
// 是否掛起
if curThread.Suspended then
begin
temp := temp + '是否掛起: True' + #13#10;
end
else
begin
temp := temp + '是否掛起: False' + #13#10;
end;
// 是否可以執行任務
if curThread.FIsCanTask then
begin
temp := temp + '是否可以執行: True' + #13#10;
end
else
begin
temp := temp + '是否可以執行: False' + #13#10;
end;
// 是否同步執行任務
if curThread.FIsSynchronize then
begin
temp := temp + '是否同步執行: True' + #13#10;
end
else
begin
temp := temp + '是否同步執行: False' + #13#10;
end;
temp := temp + #13#10;
end;
finally
Self.EndRead;
end;
Result := Trim(temp);
end;
// -----------------------------------------------------------------------------
// 執行任務線程構造函數
constructor TExecuteThread.Create(CreateSuspended: Boolean);
begin
inherited Create(CreateSuspended);
FreeOnTerminate := true;
Self.FIsCanTask := false; // 是否可以執行任務默認值爲false
Self.FIsSynchronize := false; // 是否同步執行默認值爲false
end;
// 顯示線程編號(測試使用)
procedure TExecuteThread.showThreadID;
begin
with Form1 do
begin
Memo1.Lines.Add('停止執行任務線程編號:' + inttostr(Self.ThreadID))
end;
end;
// 執行任務線程的主方法
procedure TExecuteThread.Execute;
begin
while not Terminated do
begin
if Terminated then
begin
Break;
end;
// 判斷可以執行任務
if Self.FIsCanTask then
begin
Self.FProc(); // 執行任務
end;
// 判斷不執行任務
if ThreadPool.Instance.DoNextTask(Self) = false then
begin
// 顯示執行任務線程編號
Synchronize(Self.showThreadID); // 測試使用,正式使用刪除
// 判斷掛起當前線程
if ThreadPool.Instance.IsSuspend(Self) then
begin
Self.Suspend; // 掛起
end
else // 不掛起則終止當前線程
begin
Self.Terminate; // 終止
end;
end;
// 使界面有反應
Application.ProcessMessages;
end;
end;
// 設置要執行的任務
procedure TExecuteThread.StartTask(task: TaskRec);
begin
Self.FProc := task.TaskProc; // 設置要執行的任務
Self.FIsSynchronize := task.isSynchronize; // 設置是否同步執行
Self.FIsCanTask := true; // 設置是否可以執行任務爲true
end;
end.
演示例子代碼:
unit MainUnit;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, System.DateUtils,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
Button2: TButton;
Button3: TButton;
Button7: TButton;
teskCountEdt: TEdit;
Button8: TButton;
useTimeLab: TLabel;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
procedure Button7Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button8Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure log(lgoInfo: string); // log方法
end;
var
Form1: TForm1;
repeatCount: Integer = 0;
startTime: TDateTime; // 開始時間
useTime: Double; // 用時
implementation
{$R *.dfm}
uses ThreadPoolUint;
procedure TaskFun;
var
count: Integer;
begin
// with Form1 do
// begin
//
// inc(repeatCount);
//
// Memo1.Lines.Add(FormatDateTime('yyyy-mm-dd hh:mm:ss', Now) +
// ' repeat count-' + IntToStr(repeatCount));
//
// // count := 50000;
// //
// // while count > 0 do
// // begin
// // Dec(count);
// // end;
//
// end;
count := 0;
while count < 100000 do
begin
inc(count);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ThreadPool.Instance(5, 20);
self.log('線程池創建了');
end;
procedure TForm1.Button2Click(Sender: TObject);
var
task: TaskRec;
I: Integer;
timeStr: string;
posInt: Integer;
begin
startTime := Now;
useTimeLab.Caption := '0';
// 演示代碼開始-----------------------
// 循環添加要執行的任務
// 1,添加要執行任務
for I := 0 to StrToInt(teskCountEdt.Text) - 1 do
begin
// 執行任務記錄
task.isSynchronize := false;
task.TaskProc := TaskFun;
// 添加要執行的任務
ThreadPool.Instance.AddTask(task);
end;
// 2,讓線程池執行任務
ThreadPool.Instance.ExecuteTask;
// 演示代碼結束-----------------------
useTime := MilliSecondSpan(startTime, Now);
timeStr := FloatToStr(useTime);
posInt := Pos('.', timeStr);
Delete(timeStr, posInt, Length(timeStr) - (posInt - 1));
useTimeLab.Caption := '共用時: ' + timeStr + ' 毫秒';
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
self.log(ThreadPool.Instance.GetPoolState); // 顯示線程池狀態
end;
procedure TForm1.Button7Click(Sender: TObject);
begin
ThreadPool.ReleaseInstance;
self.log('線程池釋放了');
end;
procedure TForm1.Button8Click(Sender: TObject);
begin
Memo1.Clear;
repeatCount := 0;
useTimeLab.Caption := '0';
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
ThreadPool.ReleaseInstance;
end;
procedure TForm1.log(lgoInfo: string);
begin
Memo1.Lines.Add('');
Memo1.Lines.Add(FormatDateTime('yyyy-mm-dd hh:mm:ss', Now) + ' ' +
trim(lgoInfo))
end;
end.
調用線程池的代碼是:
//1,定義一個要執行任務的結構
task.isSynchronize := false;//是否同步執行
task.TaskProc := TaskFun;//要執行任務方法
// 2,向線程池添加要執行的任務
ThreadPool.Instance.AddTask(task);
// 3,讓線程池執行任務
ThreadPool.Instance.ExecuteTask;