1

I have a TThread Class that can run independently and terminates and frees itself after it's done. I considered the termination and everything works. The problem is, that I would like to add a feature that the user can select and choose how many SYNCHRONOUS threads should be active at the same time. An example would be:

  • The program has to do 100 total tasks!
  • The user chooses 3 Threads should be running at the same time to complete all the tasks.

The first step I did was to create 3 instances of my TThread Class and the resume them in a for loop. So 3 threads are running. After the first thread is done (or terminated), another new instance needs to be created and resumed.

I get stuck on this point and I wonder how I can realize this. Any advice would be helpful.

Edit: Some Code

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

type
  TMyThread = class (TThread)
  public
  procedure Execute; override;
end;

var
  Form1       : TForm1;
  Threads     : Integer = 3;
  TotalTasks  : Integer = 100;

implementation

{$R *.dfm}

procedure TMyThread.Execute;
begin
  // some work...
  sleep (2000 + random (5000));
end;

function DummyThread ( p : pointer ) : Integer; stdcall;
var
  NewInstanceOfTMyThread  : TMyThread;
  I                       : Integer;
begin
  for I := 1 to Threads do begin
    with TMyThread.Create (TRUE) do begin
      resume;
    end;
  end;
  // Here should be code to detect if a new thread has to be started, etc.
end;

// We start the task to start the tasks...
procedure TForm1.Button1Click(Sender: TObject);
var
  ThreadID    : DWORD;
begin
  CloseHandle (CreateThread(NIL, 0, @DummyThread, NIL, 0, ThreadID));
end;

end.
Ben
  • 3,380
  • 2
  • 44
  • 98
  • 1
    there are many ways to handle, not self terminating threads which would get a new job, handling in a threadmanager, handling by OnTerminate .... http://stackoverflow.com/q/9025022/1699210 – bummi May 17 '13 at 23:33
  • 4
    You appear to be doing this the wrong way. You need a pool of threads, and a list of tasks. The threads consume the tasks until there are no tasks left. – David Heffernan May 18 '13 at 06:51
  • 4
    Please note well the solution in @DavidHeffernan comment. It is safer and more performant than any attempt to manage threads by continually creating/terminating/freeing them. Create your X threads ONCE and pass a producer-consumer queue in the ctor. If you want X+1 threads, just create another one. If you want X-1 threads, queue up one 'poison-pill' task that instructs the receiving thread to terminate. Other solutions are err... 'very difficult' to get right. – Martin James May 18 '13 at 07:04

1 Answers1

3

You can write a handler for OnTerminate event of TThread. The handler should start/resume a new thread.

Or you can have 3 threads which are running constantly and take tasks from some queue (just take care of synchronizing the queue access).

nullptr
  • 11,008
  • 1
  • 23
  • 18
  • But how does the instance of the TThread Class know how many threads are running and when to stop? – Ben May 17 '13 at 23:33
  • 1
    The instance of TThread shouldn't know about how many threads are running. It's the task for some other object: somebody (some Manager object) should create all the threads and assign tasks to them. In the simplest case you can just have a list of threads waiting for Resume (or list of tasks waiting to be processed), then the finished thread just takes the next thread/task from the list and resumes/processes it. The list should be common for all threads of course. – nullptr May 17 '13 at 23:39
  • I was thinking about an extra thread that checks about all the threads if there still running or now, but how do I do that with the same TThread Class Var (only 1). See my code. – Ben May 17 '13 at 23:45
  • 1
    I don't see any need in an extra thread. Well, try something like: `var WaitingThreads: TList; ListLock: TCriticalSection; { WaitingThreads and ListLock are global variables } procedure TWorkThread.Execute; begin { ... some work ... } ListLock.Enter; if WaitingThreads.Count > 0 then begin TThread(WaitingThreads[0]).Resume; WaitingThreads.Delete(0); end; ListLock.Leave; end;` Then fill the list with 97 suspended TThread objects and create also 3 running TThread objects. Although keeping 100 TThreads doesn't look optimal to me, I'd prefer only 3 threads and list of tasks. – nullptr May 17 '13 at 23:51
  • You will need an extra thread. I could queue all the tasks as threads first, but that would cost a lot of memory and would be more difficult to handle. But still a good idea. – Ben May 17 '13 at 23:55
  • 1
    Queueing all the tasks as threads is what you do in your `for` loop. Ok, then make a list of tasks and create a new thread only when a task is processed (the code is just as in previous comment, but with an extra TThread.Create and assigning a task from the List - although there is no need to create a new thread, we can just continue with the new task in the current thread). I still don't see an extra thread here. There is no need to look after working threads, they are looking after them themselves (provided that your application will not terminate until all threads are done). – nullptr May 18 '13 at 00:04