Delphi線程池(Delphi2009以上版本適用)

在網上查找Delphi線程池,結果發現寥寥無幾。看了半天源代碼,弄得一頭霧水,覺得不容易理解和使用,於是自己想寫一個線程池。什麼樣的線程池更好呢?我覺得使用起來要可靠,並且一定要簡單,這樣纔是更好的。我寫的線程池就是這樣一個標準,使用非常簡單,只傳入自己要執行的方法就可以了,其實大家最後就是關注自己要操作的方法,其餘的交給線程池。全部源代碼如下:

{
  //單元: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;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章