Viktor Anastasov
Viktor Anastasov

Reputation: 1113

How to wait for multiple threads to terminate in delphi?

I have an application with 50 threads that do something and I want my procedure (btnTestClick) to wait until all threads are terminated. I've tried with global variable as counter and with WaitForMultipleObjects(threadCount, @threadArr, True, INFINITE); but the procedure never waits for all threads to terminate.Here is how my thread looks like :

TClientThread = class(TThread)
  protected
    procedure Execute; override;
public
  constructor create(isSuspended : Boolean = False);
end;

And here are the constructor and the execute procedure :

constructor TClientThread.create(isSuspended : Boolean);
begin
  inherited Create(isSuspended);
  FreeOnTerminate := True;
end;

procedure TClientThread.Execute;
var
begin
  inherited;
  criticalSection.Enter;
  try
    Inc(globalThreadCounter);
  finally
    criticalSection.Leave;
  end;
end; 

And my OnButtonClick is this :

procedure TMainForm.btnTestClick(Sender: TObject);
var
  clientThreads : array of TClientThread;
  i, clientThreadsNum : Integer;
begin
  clientThreadsNum := 50;
  SetLength(clientThreads, clientThreadsNum);
  for i := 0 to Length(clientThreads) - 1 do begin // РЕДАКТИРАЙ !!
    clientThreads[i] := TClientThread.Create(True);
  end;
  // here I will assign some variables to the suspended threads, but that's not important here 
  for i := 0 to Length(clientThreads) - 1 do begin
    clientThreads[i].Start;
  end;

  WaitForMultipleObjects(clientThreadsNum, @clientThreads, True, INFINITE);
  // do something after all threads are terminated 
end;

Upvotes: 5

Views: 5099

Answers (1)

David Heffernan
David Heffernan

Reputation: 612804

You code reads:

WaitForMultipleObjects(clientThreadsNum, @clientThreads, True, INFINITE);

where clientThreads is of type:

array of TClientThread

Now, @clientThreads is the address of the dynamic array. That is the address of a pointer to the first thread object. But you are expected to pass a pointer to the first thread handle, something utterly different. So instead you need to form a list of thread handles:

var
  ThreadHandles: array of THandle;
....
SetLength(ThreadHandles, Length(clientThreads));
for i := 0 to high(clientThreads) do
  ThreadHandles[i] := clientThreads[i].Handle;
WaitForMultipleObjects(clientThreadsNum, Pointer(ThreadHandles), True, INFINITE);

You would have discovered that your call to WaitForMultipleObjects was incorrect had you checked the return value. A basic rule of Win32 programming, one that you should strive not to break, is that you check the values returned by function calls.

I believe that your call to WaitForMultipleObjects will return WAIT_FAILED. When that happens you can call GetLastError to find out what went wrong. You should modify your code to perform proper error checking. Take good care to read the documentation to find out how to do so.

Upvotes: 16

Related Questions