Managing dynamic number of threads

前端 未结 3 439
北恋
北恋 2021-01-05 06:18

First of all, I\'m still familiarizing myself with multi-threading, and don\'t know much terminology. I need to make sure I\'m doing this right, because it\'s a sensitive su

相关标签:
3条回答
  • 2021-01-05 06:47

    As explained by @NGLN etc, you need to pool some threads and accept that the easiest way to manage thread numbers is to divorce the actual number of threads from the desired number. Adding threads to the pool is easy - just create some more instances, (passing the producer-consumer task input queue as a parameter so that the thread knows what to wait on). If the desired number of threads is less than that currently existing, you could queue up enough 'poison-pills' to kill off the extra threads.

    Don't keep any list of thread pointers - it's a load of micro-management hassle that's just not necessary, (and will probably go wrong). All you need to keep is a count of the number of desired threads in the pool so you know what action to take when something changes the 'poolDepth' property.

    The event triggers are best loaded into the jobs that are issued to the pool - descend them all from some 'TpooledTask' class that takes an event as a constructor parameter and stores it in some 'FonComplete' TNotifyEvent. The thread that runs the task can call the FonComplete when it's done the job, (with the TpooledTask as the sender parameter) - you don't need to know what thread ran the task.

    Example:

        unit ThreadPool;
    
        interface
    
        uses
          Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
          Dialogs, StdCtrls, contnrs, syncobjs;
    
    
        type
    
        TpooledTask=class(TObject)
        private
          FonComplete:TNotifyEvent;
        protected
          Fparam:TObject;
          procedure execute; virtual; abstract;
        public
          constructor create(onComplete:TNotifyEvent;param:TObject);
        end;
    
        TThreadPool=class(TObjectQueue)
        private
          access:TcriticalSection;
          taskCounter:THandle;
          threadCount:integer;
        public
          constructor create(initThreads:integer);
          procedure addTask(aTask:TpooledTask);
        end;
    
        TpoolThread=class(Tthread)
        private
          FmyPool:TThreadPool;
        protected
          procedure Execute; override;
        public
          constructor create(pool:TThreadPool);
        end;
    
        implementation
    
        { TpooledTask }
    
        constructor TpooledTask.create(onComplete: TNotifyEvent; param: TObject);
        begin
          FonComplete:=onComplete;
          Fparam:=param;
        end;
    
        { TThreadPool }
    
        procedure TThreadPool.addTask(aTask: TpooledTask);
        begin
          access.acquire;
          try
            push(aTask);
          finally
            access.release;
          end;
          releaseSemaphore(taskCounter,1,nil); // release one unit to semaphore
        end;
    
        constructor TThreadPool.create(initThreads: integer);
        begin
          inherited create;
          access:=TcriticalSection.create;
          taskCounter:=createSemaphore(nil,0,maxInt,'');
          while(threadCount<initThreads) do
          begin
            TpoolThread.create(self);
            inc(threadCount);
          end;
        end;
    
        { TpoolThread }
    
        constructor TpoolThread.create(pool: TThreadPool);
        begin
          inherited create(true);
          FmyPool:=pool;
          FreeOnTerminate:=true;
          resume;
        end;
    
    procedure TpoolThread.execute;
    var thisTask:TpooledTask;
    begin
      while (WAIT_OBJECT_0=waitForSingleObject(FmyPool.taskCounter,INFINITE)) do
      begin
        FmyPool.access.acquire;
        try
          thisTask:=TpooledTask(FmyPool.pop);
        finally
          FmyPool.access.release;
        end;
        thisTask.execute;
        if assigned(thisTask.FonComplete) then thisTask.FonComplete(thisTask);
      end;
    end;
    
    end.
    
    0 讨论(0)
  • 2021-01-05 06:49

    About your problem with the decrement of active threads: sorry, but you simply have to decide for yourself. Either free the unwanted threads immediately (which terminates them at the earliest possible moment), or let them run until they are finished (which terminates them after all work is done). It is your choice. Of course you have to separate the variable for the wished number from that of the actual number of threads. The problem for updating the actual number of threads variable (could simply be a List.Count) are for both exactly the same since either solution will require some time.

    And on management of multiple threads: you can study this answer which stores the threads in a TList. It needs a little tweaking for you specific wish list though, please shout in case of need of assistance with that. Also, there are of course more possible implementations which can be derived from the use of the default TThread. And note that there exist other (multi)thread libraries, but I have never had the need to use them.

    0 讨论(0)
  • 2021-01-05 06:58

    You can implement FreeNotify Message in your request queue and when Worker Thread receieve this message free themselves. In your example when you decrease number of threads from 5 to 3 just put 2 FreeNotify messages in your queue and 2 worker threads will be free.

    0 讨论(0)
提交回复
热议问题