Delphi ThreadPool 线程池,Delphi2009以上版本适用

http://blog.sina.com.cn/s/blog_6250a9df0101kref.html

在网上查找Delphi线程池,结果发现寥寥无几。

看了半天源代码,弄得一头雾水,觉得不容易理解和使用,于是自己想写一个线程池。

什么样的线程池更好呢?

我觉得使用起来要可靠,并且一定要简单,这样才是更好的。

我写的线程池就是这样一个标准,使用非常简单,只传入自己要执行的方法就可以了,

其实大家最后就是关注自己要操作的方法,其余的交给线程池。全部源代码如下:

{
  {单元:ThreadPoolUint}
  {说明:线程池}
  //
  {Rev. 开发日期      开发者   EMail}
  {Ver.1.0.0   2011/05/05    孙玉良   sunylat@gmail.com}
}

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;

如果您有任何建议,请联系:sunylat@gmail.com QQ:14667479