线程池任务队列

http://blog.csdn.net/fengsh998/article/details/8007009


unit uPool;

{***********************************************************************

                       线程池+任务队列

         整个线程池调度图
         ==========================================================
         |  -----   ----------------------                         |
         |  |空 |   | 任务队列   ←----    | ⑴                     |
         |  |闲 |   ----------------------                         |
         |  |线 |     ↑空闲线程检查队列是否有任务                  |
         |  |程 |--①-- 有任务要执行时,加入到工作队列              |
         |  |队 |            |                                     |
         |  |列 |            ↓②               ----------------    |
         |  |   |   -----------------------    |  自动回收空   |   |
         |  |   |   |正在工作队列          |   |  闲定时器     |   |
         |  |   |   -----------------------    ----------------    |
         |  |   |     ③     | 任务做完后              |           |
         |  ----- ←----------| 调度到空闲队列          |           |
         |    |                                        |           |
         |    -----------------------------------------|           |
         |             ④定时回收空闲线程                          |
         |                                                         |
         ==========================================================

         使用方法:

         pool = TThreadPool.Create;
         pool.MinNums := 2; //最小线程
         pool.MaxNums := 6; //最大线程
         pool.TasksCacheSize := 10; //任务缓冲队列

         上面创建好之后,就可以往池中放任务

         pool.AddWorkTask(Task);

         线程池就开始工作了。
         同时线程池支持对任务进行优先级排序,排序算法默认
         为快速序,也可以外问进行队列排序

         这里把任务和池分开了。
         使用任务时,需要继承TWorkTask进开自己的任务设计。
         然后重写exectask;方法。如果方法中要进行毫时循环,
         请见如下例子;
         for i := 0 to 5000 do
          begin
            if tk.WorkState = tsFinished then break;
              inc(k);
              //caption := inttostr(k);
            edit2.Text := inttostr(k);
          end;

         如:TWirteFileTask = Class(TWorkTask);


        9-23:
        修正BUG
          1.在设置MIN时多创建了线程。
          2.定时回收机制BUG。
          3.外部处理预任务时线程不能自增。

        作者:边缘
        @RightCopy fsh
        QQ: 19985430
        date: 2012-09-22
        Email:fengsh998@163.com
***********************************************************************}

interface

uses
    Classes,Windows,SysUtils,Messages,SyncObjs;

Const
    PRE_NUM = 5;
    MAX_NUM = 100;
    AUTO_FREE = 2;
    MAX_TASKNUM = 100;
    ONEMINUTE = 10000;//60000;

  type
    TLogLevel = (lDebug,lInfo,lError);

    ILog = interface
      procedure WriteLog(Const Msg:String;Level:TLogLevel = lDebug);
    end;

    TPoolLog = Class(TInterfacedObject,ILog)
       private
          procedure WriteLog(Const Msg:String;Level:TLogLevel = lDebug);
       public
          procedure OutputLog(Const Msg:String;Level:TLogLevel);virtual;
    End;

    TPoolException = class(Exception)

    end;

    Thandles = Array of Cardinal;

    //任务级别  优先级高的任务先执行。
    TTaskLevel = (tlLower,tlNormal,tlHigh);
    TTaskState = (tsNone,tsDoing,tsWaiting,tsReStart,tsStop,tsFinished);
    TWorkTask = Class
       private
          Work:TThread;
          //任务ID
          hTask:TCriticalSection;
          FWorkId:Cardinal;
          FWorkName:String;
          FWorkLevel:TTaskLevel; //默认为普通
          FWorkState : TTaskState;
          procedure setWorkState(Const Value:TTaskState);
       public
          Constructor Create;
          Destructor Destroy;override;
          procedure execTask;virtual; abstract;
          property WorkId:Cardinal read FWorkId write FWorkId;
          property WorkName:String read FWorkName write FWorkName;
          property WorkLevel:TTaskLevel read FWorkLevel write FWorkLevel;
          property WorkState : TTaskState read FWorkState write setWorkState;
    End;

    TWorkTaskQueue = Array of TWorkTask;
    
    TThreadPool = Class;

    TWorkThreadState = (wtIdle,wtRunning,wtStop,wtFinished);
    //工作线程(单个线程一次只能处理一个task)
    TWorkThread = Class(TThread)
      private
        FPool:TThreadPool;
        FState:TWorkThreadState;
        procedure SetDefault;
      protected
        procedure Execute;override;
      public
        Constructor Create(Const pool:TThreadPool);
        property State : TWorkThreadState read FState write FState;
    End;

    TWorkThreadQueue = Array of TWorkThread;

    //查看缓冲情况事件
    TListenCacheInfoEvent = procedure (Sender:TObject;Const IdleCount,BusyCount,TaskCount:Integer) of Object;
    TTaskQueueFullEvent = procedure (Sender:TObject) of Object;
    //任务处理完后
    TTaskFinishedEvent = procedure (Const cTast:TWorkTask) of object;
    //任务准备被处理前事件
    TTaskWillDoBeforeEvent = procedure (Const thId:Cardinal;Const cTast:TWorkTask) of Object;
    //外部排序任务队列算法,默认为快速排序,可自行在外部定制算法。
    TSortTaskQueueEvent = procedure (Sender:TObject;var taskQueue:TWorkTaskQueue) of object;

    TThreadPool = Class
     private
       Log:TPoolLog;
       //自动回收标识
       FAuto:Boolean;
       //定时等待控制
       FWaitFlag:Boolean;
       //表示正在用于等待回收到的线程
       Waiting:TWorkThread;
       //提取任务通知信号
       entTaskNotify:Tevent;
       //时间事件HANDLE
       hTimeJump:Cardinal;
       //是否排序任务队列
       FSorted:Boolean;
       //对空闲队列操作锁
       hIDleLock:TCriticalSection;
       //对正在进行的线程锁
       hBusyLock:TCriticalSection;
       //任务队列锁
       hTaskLock:TCriticalSection;
       //预设线程数 默认为5 发现忙不过来时才进行自增直到Max
       FMinNums:Integer;
       //最大限制线程数,默认为100
       FMaxNums:Integer;
       //任务队列缓冲大小 默认100
       FTasksCache:Integer;
       //当线程空闲时间长达XX时自动回收 :单位为分钟
       FRecoverInterval:Integer;
       //是否允许队列中存在重复任务 (同一任务时要考虑线程同步),默认为否
       FIsAllowTheSameTask:Boolean;
       //任务队列 (不释放外部任务) 最大100个任务。当大于100个任务时,需要等待
       //每抽取一个任务,立即从队列中删除。
       TaskQueue:TWorkTaskQueue;
       //工作线程
       BusyQueue:TWorkThreadQueue;
       //空闲线程
       IdleQueue:TWorkThreadQueue;

       //************************事件回调**********************//
       //排序队列回调
       FOnSortTask:TSortTaskQueueEvent;
       FOnTaskWillDo:TTaskWillDoBeforeEvent;
       FOnTaskFinished:TTaskFinishedEvent;
       FOnTaskFull:TTaskQueueFullEvent;
       FOnListenInfo:TListenCacheInfoEvent;
       //*****************************************************//
       
       //************************Get/Set操作*******************//
       procedure SetMinNums(Const Value:Integer);
       procedure SetMaxNums(Const Value:Integer);
       function getTaskQueueCount: Integer;
       function getBusyQueueCount: Integer;
       function getIdleQueueCount: Integer;
       //*****************************************************//

       //***********************同步量处理********************//
       procedure CreateLock;
       procedure FreeLock;
       //*****************************************************//

       //设置初值
       procedure SetDefault;
       //处理回调
       procedure DoTaskFull;

       //********************线程队列操作**********************//
       //清空线程队列
       procedure ClearQueue(var Queue:TWorkThreadQueue);
       //得到队列的长度
       function QueueSize(Const Queue:TWorkThreadQueue):Integer;
       //调整队列
       procedure DelQueueOfIndex(var Queue:TWorkThreadQueue;Const Index:Integer);
       //移动队列;
       procedure MoveQueue(Const wt:TWorkThread;flag:Integer);
       //移除某个线程
       procedure RemoveFromQueue(var Queue:TWorkThreadQueue;Const re:TWorkThread);
       //*****************************************************//

       //********************任务队列操作**********************//
       //排序队列将优先级高的排前面。//可以交给外问进行排序算法
       procedure SortTask(var Queue:TWorkTaskQueue);
       //调整队列
       procedure DelTaskOfIndex(var Queue:TWorkTaskQueue;Const Index:Integer);
       //获取队列大小
       function TaskSzie(Const Queue:TWorkTaskQueue):Integer;
       //*****************************************************//
       //查找任务(如果有好的算法,哪更高效)
       function FindTask(Const tsk:TWorkTask):Integer;
       //快速排序
       procedure QuikeSortTask(var Queue:TWorkTaskQueue;Const s,e:Integer);
       //自动回收空闲线程
       procedure RecoverIDle(Const wait:TWorkThread);
       //交换任务
       procedure switch(var Queue: TWorkTaskQueue; m, n: Integer);
       //判断当前运行线程是否使用在等待自动回收
       function WaitAutoRecover(Const curThread:TWorkThread):Boolean;
     protected
       //求最小值
       function Smaller(Const expresion:Boolean;Const tureValue,falseValue:Integer):Integer;
       //按照先进选出进行提取任务
       function PickupTask:TWorkTask;
       //创建空闲线程
       procedure CreateIdleThread(Const Nums:Integer = 1);
       //添加到空闲线程队列
       procedure AddThreadToIdleQueue(Const idle:TWorkThread);
       //添加到工作队列
       procedure AddThreadToBusyQueue(Const busy:TWorkThread);
       //发送新任务到达信号
       procedure PostNewTaskSign;
       
     public
       Constructor Create;
       Destructor Destroy;override;
       //***********************线程池管理方法******************************//
       //停止执行的任务
       procedure StopAll;
       //开始任务
       procedure StartAll;
       //清空任务
       procedure CleanTasks;
       //运行中不能进行对调
       function  SwitchTasks(Const aTask,bTask:TWorkTask):Boolean;
       //移除某个任务
       procedure RemoveTask(Const tk:TWorkTask);//只允许移除未执行的任务
       //需要外部定时调用来得到动态数据效果
       procedure ListenPool;
       //******************************************************************//
       //添加任务
       function AddWorkTask(Const wtask:TWorkTask):Integer;

       property MinNums:Integer read FMinNums write SetMinNums;
       property MaxNums:Integer read FMaxNums write SetMaxNums;
       property TasksCacheSize:Integer read FTasksCache write FTasksCache;
       property RecoverInterval:Integer read FRecoverInterval
                write FRecoverInterval;
       property IsAllowTheSameTask:Boolean read FIsAllowTheSameTask
                write FIsAllowTheSameTask;
       property Sorted:Boolean read FSorted write FSorted;
       property TaskQueueCount:Integer read getTaskQueueCount;
       property IdleQueueCount:Integer read getIdleQueueCount;
       property BusyQueueCount:Integer read getBusyQueueCount;
       property OnSortTask:TSortTaskQueueEvent read FOnSortTask write FOnSortTask;
       property OnTaskWillDo:TTaskWillDoBeforeEvent read FOnTaskWillDo write FOnTaskWillDo;
       property OnTaskFinished:TTaskFinishedEvent read FOnTaskFinished write FOnTaskFinished;
       property OnTaskFull:TTaskQueueFullEvent read FOnTaskFull write FOnTaskFull;
       property OnListenInfo:TListenCacheInfoEvent read FOnListenInfo write FOnListenInfo;
    End;

implementation

{ TThreadPool }

constructor TThreadPool.Create;
var
  tpError:Cardinal;
begin
   Log:=TPoolLog.Create;
   SetDefault;
   CreateLock;

   tpError := 0;

   entTaskNotify:=Tevent.create(nil,false,false, 'TaskNotify');//事件信号
   hTimeJump := CreateEvent(nil,False,False,'Timer');//自动回收心跳事件
   if hTimeJump = 0 then
       tpError := GetLastError;
       
   //the same name of sign exists.
   Case tpError of
     ERROR_ALREADY_EXISTS:
                       begin
                          hTimeJump := 0;
                          Log.WriteLog('CreateTimerEvent Fail,the Same Name of Event Exists');
                       end;
   End;
   //预创建线程
   CreateIdleThread(FMinNums);
   Log.WriteLog('Thread Pool start run.',lInfo);
end;

destructor TThreadPool.Destroy;
begin
   ClearQueue(IdleQueue);
   ClearQueue(BusyQueue);
   FreeLock;
   if hTimeJump > 0 then
      CloseHandle(hTimeJump);
   entTaskNotify.Free;
   Log.Free;
  inherited;
  Log.WriteLog('Thread Pool end run.',lInfo);
end;

procedure TThreadPool.DoTaskFull;
begin
   if Assigned(FOnTaskFull) then
      FOnTaskFull(self);
end;

procedure TThreadPool.SetDefault;
begin
   FMinNums := PRE_NUM;
   FMaxNums := MAX_NUM;
   FTasksCache := MAX_TASKNUM;
   FRecoverInterval := AUTO_FREE;
   FIsAllowTheSameTask := False;
   FAuto :=False;
   FWaitFlag := True;
   Waiting := nil;
   FSorted := False;
end;

procedure TThreadPool.CreateLock;
begin
   hIDleLock := TCriticalSection.Create;
   hBusyLock := TCriticalSection.Create;
   hTaskLock := TCriticalSection.Create;
end;

procedure TThreadPool.FreeLock;
begin
   hIDleLock.Free;
   hBusyLock.Free;
   hTaskLock.Free;
end;

function TThreadPool.getBusyQueueCount: Integer;
begin
   Result := QueueSize(BusyQueue);
end;

function TThreadPool.getIdleQueueCount: Integer;
begin
   Result := QueueSize(IdleQueue);
end;

function TThreadPool.getTaskQueueCount: Integer;
begin
   Result := TaskSzie(TaskQueue);
end;

procedure TThreadPool.CleanTasks;
begin
   hTaskLock.Enter;
   SetLength(TaskQueue,0);
   hTaskLock.Leave;
end;

procedure TThreadPool.ListenPool;
begin
   //正在执行任务的线程,空闲线程,队列中任务数
   if Assigned(FOnListenInfo) then
      FOnListenInfo(self,IdleQueueCount,BusyQueueCount,TaskQueueCount);
end;

procedure TThreadPool.ClearQueue(var Queue: TWorkThreadQueue);
var
   i:Integer;
   sc:Integer;
begin
   sc := Length(Queue);
   for i := 0 to sc - 1 do
   begin
       TWorkThread(Queue[i]).Terminate;
       PostNewTaskSign;
       //TWorkThread(Queue[i]).Free; //如果FreeOnTerminate为TRUE就不要使用这句了。
   end;
   SetLength(Queue,0);
end;

procedure TThreadPool.SetMaxNums(const Value: Integer);
begin
   if Value<FMinNums then
      //to do tips Error;
   else
      FMaxNums := Value;
end;

procedure TThreadPool.SetMinNums(const Value: Integer);
begin
   if value > FMaxNums then
      //to do tips Error;
   else if Value <= 0 then
      FMinNums := PRE_NUM
   else
      FMinNums := Value;
     
   ClearQueue(IDleQueue);
   CreateIdleThread(FMinNums);
   Log.WriteLog('Reset MinNums Numbers is ' + inttostr(FMinNums) + ' .',lInfo);
end;


function TThreadPool.Smaller(const expresion: Boolean; const tureValue,
  falseValue: Integer): Integer;
begin
   if expresion then
      result := tureValue
   else
      result := falseValue;
end;

procedure TThreadPool.DelQueueOfIndex(var Queue: TWorkThreadQueue;
  const Index: Integer);
var
   i:integer;
   ic:integer;
begin
   ic := Length(Queue);
   for i := Index to ic - 1 do
       Queue[i] := Queue[i+1];

   setLength(Queue,ic-1);
end;

procedure TThreadPool.DelTaskOfIndex(var Queue: TWorkTaskQueue;
  const Index: Integer);
var
   i:integer;
   ic:integer;
begin
   ic := length(Queue);
   for i := Index to ic -1 do
       Queue[i] := Queue[i+1];

   setLength(Queue,ic-1);
end;

procedure TThreadPool.MoveQueue(const wt: TWorkThread; flag: Integer);
var
    k:integer;
begin
   if flag = 0 then
   begin
     hIDleLock.Enter;
     for k := Low(IdleQueue) to High(IdleQueue) do
     begin
        if IdleQueue[k]=wt then
        begin
           AddThreadToBusyQueue(wt);
           DelQueueOfIndex(IdleQueue,k);
        end;
     end;
     hIDleLock.Leave;
   end
   else
   begin
     hBusyLock.Enter;
     for k := Low(BusyQueue) to High(BusyQueue) do
     begin
        if BusyQueue[k]=wt then
        begin
           AddThreadToIdleQueue(wt);
           DelQueueOfIndex(BusyQueue,k);
        end;
     end;
     hBusyLock.Leave;
   end;
end;

function TThreadPool.SwitchTasks(const aTask, bTask: TWorkTask): Boolean;
var
   aIndex,bIndex:Integer;
begin
   Result := true;
   hTaskLock.Enter;
   aIndex := FindTask(aTask);
   bIndex := FindTask(bTask);
   
   if (aIndex = -1) or (bIndex = -1) then
   begin
      Result := false;
      hTaskLock.Leave;
      exit;
   end;
   switch(TaskQueue,aIndex,bIndex);
   hTaskLock.Leave;
end;

function TThreadPool.TaskSzie(const Queue: TWorkTaskQueue): Integer;
begin
   Result := Length(Queue);
end;

function TThreadPool.WaitAutoRecover(const curThread: TWorkThread): Boolean;
begin
   Result := Waiting = curThread;
end;

procedure TThreadPool.CreateIdleThread(const Nums: Integer);
var
   WorkThread:TWorkThread;
   i:integer;
begin
   hIDleLock.Enter;
   for i := 0 to Nums - 1 do
   begin
     WorkThread := TWorkThread.Create(self);
     WorkThread.FreeOnTerminate := true;
     AddThreadToIdleQueue(WorkThread);
   end;
   hIDleLock.Leave;
end;

procedure TThreadPool.AddThreadToBusyQueue(const busy: TWorkThread);
var
   sz:integer;
begin
   sz := QueueSize(BusyQueue);
   setLength(BusyQueue,sz + 1);
   BusyQueue[sz] := busy;
end;

procedure TThreadPool.AddThreadToIdleQueue(const idle: TWorkThread);
var
   sz:integer;
begin
   sz := Length(IdleQueue);
   setLength(IdleQueue,sz + 1);
   IdleQueue[sz] := idle;
end;

function TThreadPool.PickupTask: TWorkTask;
begin
   //先排序再取
   hTaskLock.enter;

   if FSorted then
      SortTask(TaskQueue);

   if length(TaskQueue) > 0 then
   begin
      Result := TaskQueue[0];
      DelTaskOfIndex(TaskQueue,0);
   end
   else
      Result := Nil;

   hTaskLock.Leave;
end;

function TThreadPool.AddWorkTask(Const wtask: TWorkTask):Integer;
var
   sz,ic,bc:Integer;
begin
   sz := Length(TaskQueue);
   if sz >= FTasksCache  then
   begin
      Result := -1;
      DoTaskFull;
      exit;
   end;

   setLength(TaskQueue,sz+1);
   wtask.WorkState := tsWaiting;
   TaskQueue[sz] := wtask;

   Result := sz + 1;

   //未达到最大线程数时增加
   ic := IdleQueueCount;
   bc := BusyQueueCount;

   //最大只能ic + bc = MaxNums
   if (ic = 0) and (ic+ bc < FMaxNums) then
      CreateIdleThread();
      
   FAuto := True;
   //通知线程去取任务
   PostNewTaskSign;
   Log.WriteLog('Add a task to queue.',lInfo);
end;

function TThreadPool.FindTask(const tsk: TWorkTask): Integer;
var
   l:Integer;
begin
   Result := -1;
   for l := Low(TaskQueue) to High(TaskQueue) do
       if TaskQueue[l] = tsk then
       begin
         Result := l;
         Break;
       end;
end;

procedure TThreadPool.PostNewTaskSign;
begin
   entTaskNotify.SetEvent;
end;

procedure TThreadPool.switch(var Queue:TWorkTaskQueue;m,n:Integer);
var
 tem:TWorkTask;
begin
  tem := Queue[m];
  Queue[m] := Queue[n];
  Queue[n] := tem;
end;

procedure TThreadPool.QuikeSortTask(var Queue: TWorkTaskQueue; const s,
  e: Integer);
var
   key:Integer;
   k,j:Integer;
begin
   key := ord(Queue[s].WorkLevel);

   if s > e then exit;

   k := s;
   j := e;

   while (k <> j) do
   begin
     while (k < j) and (ord(Queue[j].WorkLevel) <= key) do //如果排序从小到大时改为 >=
         dec(j);
     switch(Queue,k,j);

     while (k < j) and (ord(Queue[k].WorkLevel) >= key) do //如果排序从小到大时改为 <=
         inc(k);
     Switch(Queue,j,k);
   end;

   if s < k-1 then
      QuikeSortTask(Queue,s,k-1);
   if k+1 < e then
      QuikeSortTask(Queue,k+1,e);
end;

procedure TThreadPool.SortTask(var Queue: TWorkTaskQueue);
var
   f,l:Integer;
   ic:Integer;
begin
   ic := Length(Queue);
   if ic = 0 then exit;
   
   if Assigned(FOnSortTask) then
      FOnSortTask(self,Queue)
   else
   begin
      f := 0;
      l := ic-1;
      QuikeSortTask(Queue,f,l);
   end;
end;

procedure TThreadPool.StartAll;
var
   I:Integer;
begin
   hBusyLock.Enter;
   for I := Low(BusyQueue) to High(BusyQueue) do
   begin
     BusyQueue[i].Resume;
     BusyQueue[i].State := wtRunning;
   end;
   hBusyLock.Leave;

   hIDleLock.Enter;
   for I := Low(IdleQueue) to High(IdleQueue) do
   begin
     IdleQueue[i].Resume;
     IdleQueue[i].State := wtRunning;
   end;
   hIDleLock.Leave;
end;

procedure TThreadPool.StopAll;
var
   I:Integer;
begin
   hBusyLock.Enter;
   for I := Low(BusyQueue) to High(BusyQueue) do
   begin
     BusyQueue[i].Suspend;
     BusyQueue[i].State := wtStop;
   end;
   hBusyLock.Leave;

   hIDleLock.Enter;
   for I := Low(IdleQueue) to High(IdleQueue) do
   begin
     IdleQueue[i].Suspend;
     IdleQueue[i].State := wtStop;
   end;
   hIDleLock.Leave;
end;

function TThreadPool.QueueSize(const Queue: TWorkThreadQueue):Integer;
begin
  Result := Length(Queue);
end;

//每次只留单线程进行空闲回收等待
procedure TThreadPool.RecoverIDle(Const wait:TWorkThread);
var
   k:Integer;
begin
   FAuto:=False;
   //等待时间超时
   FWaitFlag := False;
   Waiting := wait;
   hBusyLock.Enter;
   RemoveFromQueue(BusyQueue,wait);
   hBusyLock.Leave;
   //补给一个空闲线程
   CreateIdleThread();
   WaitforSingleObject(hTimeJump,FRecoverInterval*ONEMINUTE);

   //满足空闲时间到后并且空闲线程大于零,没有线程在执行任务,及任务队列为空
   if (IdleQueueCount > 0)
      and (BusyQueueCount = 0) //正在等待的是清空空闲线程
      and (TaskQueueCount = 0) then
   begin
      hTaskLock.Enter;
      //回收到最小设定线程
      for k := High(IdleQueue) Downto FMinNums do
      begin
         TWorkThread(IdleQueue[k]).Terminate;
         PostNewTaskSign;
      end;
      SetLength(IdleQueue,FMinNums);
      hTaskLock.Leave;
   end;
   //定时完后线程释放
   wait.Terminate;
   FWaitFlag := True;
end;

procedure TThreadPool.RemoveFromQueue(var Queue: TWorkThreadQueue;
  const re: TWorkThread);
var
   index ,i: integer;
begin
   index := -1;
   for i := Low(Queue) to High(Queue) do
   begin
       if Queue[i] = re then
       begin
          index := i;
          break;
       end;
   end;
   
   if Index<>-1 then
      DelQueueOfIndex(Queue,index);
end;

procedure TThreadPool.RemoveTask(const tk: TWorkTask);
var
   index:Integer;
begin
   index := FindTask(tk);
   if index = -1 then Exit;
   hTaskLock.Enter;
   DelTaskOfIndex(TaskQueue,index);
   hTaskLock.Leave;
end;

{ TWorkThread }

constructor TWorkThread.Create(const pool: TThreadPool);
begin
   FPool := pool;
   SetDefault;
   inherited Create(false);
end;

procedure TWorkThread.Execute;
var
  hd:Array[0..0] of Cardinal;
  ret:Cardinal;
  task:TWorkTask;
  nc:Integer;
begin
   //不断的在任务队列中取任务
   hd[0]:= fPool.entTaskNotify.Handle;
   while not Terminated do
   begin
      //跟踪时为什么会暂停不了,是因为前面在设置MinNums时有信号增加
      ret := WaitForMultipleObjects(1,@hd,false,INFINITE);
      
      if Terminated then break;

      Case ret - WAIT_OBJECT_0 of
      WAIT_OBJECT_0:
           begin
                if state <> wtRunning then 
                begin
                    try
                      //抽取一个任务
                      task := FPool.PickupTask;

                      if assigned(task) then
                      begin
                         //需要线程同步,以防正在执行的任务被其它线程执行。

                         task.hTask.Enter;
                         //当有任务做时,将自己移到工作队列中
                         fPool.MoveQueue(self,0);
                         state := wtRunning;

                         //任务启动前
                         if Assigned(fPool.FOnTaskWillDo) then
                            fPool.FOnTaskWillDo(self.ThreadID,task);

                         //指定执行线程
                         task.Work := self;
                         task.WorkState := tsDoing;
                         task.execTask;
                         state := wtFinished;
                         task.WorkState := tsFinished;
                         task.Work := nil;
                         task.hTask.leave;
                         //任务完成
                         if Assigned(fPool.FOnTaskFinished) then
                            fPool.FOnTaskFinished(task);
                      end;

                    finally

                    end;

                end;
           end;
         WAIT_OBJECT_0 + 1:;//Terminate  don't to do something
      End;

      nc := fPool.TaskQueueCount;
      if (nc > 0) then
        fpool.PostNewTaskSign
      else if (fPool.FAuto) and (fPool.FWaitFlag) and (fPool.BusyQueueCount=1) then
         fPool.RecoverIDle(self);//任务空闲,线程空闲时间大于设定时间时自动回收空闲线程

      state := wtIdle;
      //将自己移至空闲线程
      if not fPool.WaitAutoRecover(self) then //如果当前正在等待自动回收线程的
         fPool.MoveQueue(self,1)
      else
         fPool.Waiting := nil;
   end;
end;

procedure TWorkThread.SetDefault;
begin
   FState := wtIdle;
end;

{ TWorkTask }

constructor TWorkTask.Create;
begin
   hTask := TCriticalSection.Create;
   WorkState := tsNone;
   FWorkLevel := tlNormal;
   Work := nil;
end;

destructor TWorkTask.Destroy;
begin
   WorkState := tsFinished;
   if Assigned(Work) then
      Work.Resume;
   hTask.Free;
  inherited;
end;

procedure TWorkTask.setWorkState(Const Value:TTaskState);
begin

   FWorkState := Value;

   case value of
     tsReStart:
          begin
            if Assigned(Work) and (Work.Suspended)  then
            begin
                FWorkState := tsDoing;
                Work.Resume;
            end;
          end;
     tsStop:
          begin
            if Assigned(Work) then
                Work.Suspend;
          end;
   end;
end;

{ TPoolLog }

procedure TPoolLog.OutputLog(const Msg: String; Level: TLogLevel);
begin
   // to implement at sub class.
end;

procedure TPoolLog.WriteLog(const Msg: String; Level: TLogLevel);
var
   dt:TDatetime;
begin
   dt := now;
   OutputLog(datetimetostr(dt) + ' : ' + Msg,Level);
end;

end.


unit uTest;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls,uPool, StdCtrls,SyncObjs;

type

  TUICALL = procedure (Sender:TObject) of object;

  TTaskDemo = class(TWorkTask)
     private
       FOnUICall:TUICALL;
     public
       procedure execTask;override;
       property OnUICall:TUICALL read FOnUICall write FOnUICall;
  end;


  TfrmDemo = class(TForm)
    GroupBox1: TGroupBox;
    Label1: TLabel;
    edtMin: TEdit;
    Label2: TLabel;
    edtMax: TEdit;
    Label3: TLabel;
    edttasks: TEdit;
    btnset: TButton;
    GroupBox2: TGroupBox;
    Memo1: TMemo;
    Memo2: TMemo;
    ckSort: TCheckBox;
    Button1: TButton;
    GroupBox3: TGroupBox;
    GroupBox4: TGroupBox;
    GroupBox5: TGroupBox;
    GroupBox6: TGroupBox;
    GroupBox7: TGroupBox;
    GroupBox8: TGroupBox;
    GroupBox9: TGroupBox;
    GroupBox10: TGroupBox;
    GroupBox11: TGroupBox;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    cb: TComboBox;
    Timer1: TTimer;
    one: TEdit;
    two: TEdit;
    three: TEdit;
    four: TEdit;
    five: TEdit;
    six: TEdit;
    seven: TEdit;
    eight: TEdit;
    nine: TEdit;
    Button5: TButton;
    Button6: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
    procedure btnsetClick(Sender: TObject);
  private
    { Private declarations }
    kk:TCriticalSection;
  public
    { Public declarations }
    list:TList;
    tpDemo:TThreadPool;
    procedure DoTaskOne(Sender: TObject);
    procedure DoTaskTwo(Sender: TObject);
    procedure DoTaskThr(Sender: TObject);
    procedure DoTaskFour(Sender: TObject);
    procedure DoTaskFive(Sender: TObject);
    procedure DoTaskSix(Sender: TObject);
    procedure DoTaskSeven(Sender: TObject);
    procedure DoTaskEight(Sender: TObject);
    procedure DoTaskNine(Sender: TObject);
    procedure DoTaskExcuBefore(Const thId:Cardinal;Const cTast:TWorkTask);
    procedure DoListen(Sender:TObject;Const IdleCount,BusyCount,TaskCount:Integer);
  end;

var
  frmDemo: TfrmDemo;
implementation

{$R *.dfm}

procedure TfrmDemo.btnsetClick(Sender: TObject);
begin
   tpDemo.MinNums := strtoint(edtMin.text);
   tpDemo.MaxNums := strtoint(edtMax.text);
   tpDemo.TasksCacheSize := strtoint(edttasks.text);
end;

procedure TfrmDemo.Button1Click(Sender: TObject);
var
   i:integer;
   td:TTaskDemo;
begin
      list.Clear;
      cb.Clear;
        
      td:=TTaskDemo.Create;
      td.WorkId := 1;
      td.WorkName := 'Task1';
      td.WorkLevel := tlLower;
      td.OnUICall := DoTaskOne;
      Memo1.Lines.Add('id = 1 taskName = Task1');
      list.Add(td);
      cb.Items.Add(td.WorkName);
      //tpDemo.AddWorkTask(td);

      td:=TTaskDemo.Create;
      td.WorkId := 2;
      td.WorkName := 'Task2';
      td.WorkLevel := tlNormal;
      td.OnUICall := DoTaskTwo;
      Memo1.Lines.Add('id = 2 taskName = Task2');
      list.Add(td);
      cb.Items.Add(td.WorkName);
      //tpDemo.AddWorkTask(td);

      td:=TTaskDemo.Create;
      td.WorkId := 3;
      td.WorkName := 'Task3';
      td.WorkLevel := tlHigh;
      td.OnUICall := DoTaskThr;
      Memo1.Lines.Add('id = 3 taskName = Task3');
      list.Add(td);
      cb.Items.Add(td.WorkName);
      //tpDemo.AddWorkTask(td);

      td:=TTaskDemo.Create;
      td.WorkId := 4;
      td.WorkName := 'Task4';
      td.WorkLevel := tlLower;
      td.OnUICall := DoTaskFour;
      Memo1.Lines.Add('id = 4 taskName = Task4');
      list.Add(td);
      cb.Items.Add(td.WorkName);
      //tpDemo.AddWorkTask(td);

      td:=TTaskDemo.Create;
      td.WorkId := 5;
      td.WorkName := 'Task5';
      td.WorkLevel := tlNormal;
      td.OnUICall := DoTaskFive;
      Memo1.Lines.Add('id = 5 taskName = Task5');
      list.Add(td);
      cb.Items.Add(td.WorkName);
      //tpDemo.AddWorkTask(td);

      td:=TTaskDemo.Create;
      td.WorkId := 6;
      td.WorkName := 'Task6';
      td.WorkLevel := tlHigh;
      td.OnUICall := DoTaskSix;
      Memo1.Lines.Add('id = 6 taskName = Task6');
      list.Add(td);
      cb.Items.Add(td.WorkName);
      //tpDemo.AddWorkTask(td);

      td:=TTaskDemo.Create;
      td.WorkId := 7;
      td.WorkName := 'Task7';
      td.WorkLevel := tlLower;
      td.OnUICall := DoTaskSeven;
      Memo1.Lines.Add('id = 7 taskName = Task7');
      list.Add(td);
      cb.Items.Add(td.WorkName);
      //tpDemo.AddWorkTask(td);

      td:=TTaskDemo.Create;
      td.WorkId := 8;
      td.WorkName := 'Task8';
      td.WorkLevel := tlNormal;
      td.OnUICall := DoTaskEight;
      Memo1.Lines.Add('id = 8 taskName = Task8');
      list.Add(td);
      cb.Items.Add(td.WorkName);
      //tpDemo.AddWorkTask(td);

      td:=TTaskDemo.Create;
      td.WorkId := 9;
      td.WorkName := 'Task9';
      td.WorkLevel := tlHigh;
      td.OnUICall := DoTaskNine;
      Memo1.Lines.Add('id = 9 taskName = Task9');
      list.Add(td);
      cb.Items.Add(td.WorkName);
      //tpDemo.AddWorkTask(td);
                                  
      if ckSort.Checked then
         tpDemo.Sorted := true;
         
      for i := 0 to list.Count-1 do
      begin
         tpDemo.AddWorkTask(list[i]);
         sleep(200);
      end;
end;

procedure TfrmDemo.Button2Click(Sender: TObject);
begin
   tpDemo.StopAll;
   Button2.Enabled := False;
   Button3.Enabled := True;
end;

procedure TfrmDemo.Button3Click(Sender: TObject);
begin
   tpDemo.StartAll;
   Button2.Enabled := True;
   Button3.Enabled := False;
end;

procedure TfrmDemo.Button4Click(Sender: TObject);
var
   idx:integer;
begin
   idx := cb.ItemIndex;
   TWorkTask(List[idx]).WorkState := tsStop;
end;

procedure TfrmDemo.Button5Click(Sender: TObject);
var
  i:integer;
begin
      for i := 0 to list.Count-1 do
      begin
         tpDemo.AddWorkTask(list[i]);
         sleep(200);
      end;
end;

procedure TfrmDemo.Button6Click(Sender: TObject);
var
   idx:integer;
begin
   idx := cb.ItemIndex;
   TWorkTask(List[idx]).WorkState := tsReStart;
end;

procedure TfrmDemo.DoListen(Sender: TObject; const IdleCount, BusyCount,
  TaskCount: Integer);
begin
   Caption := '空闲数:'+inttostr(IdleCount)+' 工作数:'+inttostr(BusyCount)+' 任务池'+inttostr(TaskCount);
end;

procedure TfrmDemo.DoTaskEight(Sender: TObject);
var
  i:integer;
begin
   for i := 0 to 49 do
   begin
      Application.ProcessMessages;
       eight.Text := inttostr(i+1);
       sleep(1000);
   end;
end;

procedure TfrmDemo.DoTaskExcuBefore(const thId: Cardinal;
  const cTast: TWorkTask);
begin
   Memo2.Lines.Add('任务'+cTast.WorkName+'准备被执行,线程ID='+inttostr(thID));
end;

procedure TfrmDemo.DoTaskFive(Sender: TObject);
var
  i:integer;
begin
   for i := 0 to 49 do
   begin
       Application.ProcessMessages;
       five.Text := inttostr(i+1);
       sleep(1500);
   end;
end;

procedure TfrmDemo.DoTaskFour(Sender: TObject);
var
  i:integer;
begin
   for i := 0 to 49 do
   begin
       Application.ProcessMessages;
       four.Text := inttostr(i+1);
       sleep(500);
   end;

end;

procedure TfrmDemo.DoTaskNine(Sender: TObject);
var
  i:integer;
begin
   for i := 0 to 49 do
   begin
       Application.ProcessMessages;
       nine.Text := inttostr(i+1);
       sleep(700);
   end;
end;

procedure TfrmDemo.DoTaskOne(Sender: TObject);
var
  i:integer;
begin
   for i := 0 to 49 do
   begin
     Application.ProcessMessages;
     one.Text := inttostr(i+1);
     sleep(1000);
   end;

end;

procedure TfrmDemo.DoTaskSeven(Sender: TObject);
var
  i:integer;
begin
   for i := 0 to 49 do
   begin
       Application.ProcessMessages;
       seven.Text := inttostr(i+1);
       sleep(400);
   end;
end;

procedure TfrmDemo.DoTaskSix(Sender: TObject);
var
  i:integer;
begin
   for i := 0 to 49 do
   begin
       Application.ProcessMessages;
       six.Text := inttostr(i+1);
       sleep(1200);
   end;
end;

procedure TfrmDemo.DoTaskThr(Sender: TObject);
var
  i:integer;
begin
   for i := 0 to 49 do
   begin
       Application.ProcessMessages;
       three.Text := inttostr(i+1);
       sleep(1000);
   end;
end;

procedure TfrmDemo.DoTaskTwo(Sender: TObject);
var
  i:integer;
begin
   for i := 0 to 49 do
   begin
       Application.ProcessMessages;
       two.Text := inttostr(i+1);
       sleep(800);
   end;
end;

procedure TfrmDemo.FormCreate(Sender: TObject);
begin
   list:=TList.Create;
   tpDemo:=TThreadPool.Create;
   tpDemo.OnTaskWillDo := DoTaskExcuBefore;
   tpDemo.OnListenInfo := DoListen;
   //tpDemo.MinNums := 2;
   tpDemo.MaxNums := 10;
   tpDemo.TasksCacheSize := 10;
   edtMin.Text := InttoStr(tpDemo.MinNums);
   edtMax.Text := InttoStr(tpDemo.MaxNums);
   edttasks.Text := InttoStr(tpDemo.TasksCacheSize);
   Button2.Enabled := True;
   Button3.Enabled := False;
   kk:=TCriticalSection.Create;
end;

procedure TfrmDemo.FormDestroy(Sender: TObject);
begin
   kk.free;
   list.Free;
   tpDemo.Free;
end;


procedure TfrmDemo.Timer1Timer(Sender: TObject);
begin
   tpDemo.ListenPool;
end;

{ TTaskDemo }

procedure TTaskDemo.execTask;
begin
  if Assigned(FOnUICall) then
     FOnUICall(self);
end;


end.



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值