Mohammadreza
Mohammadreza

Reputation: 15

Why does this code block my main thread?

I need my Thread work independent of Form. For example I have a endless loop in my Thread:

procedure TCustomThread.doProc;
begin
  repeat
.
.
.
  until (1 = 2);
end;

procedure TCustomThread.Execute;
begin
  inherited;

  Synchronize(doProc);
end;
.
.
.
procedure TForm1.Button1Click(Sender: TObject);
var
  thrd : TCustomThread;
begin
  thrd := TCustomThread.Create(True);
  thrd.Resume;
  Application.ProcessMessages;
end;

Now, when I click on Button1, my Thread runs but main Form is locked. How can I avoid suspending Form?

Upvotes: 2

Views: 829

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 596557

TThread.Synchronize() runs the specified procedure in the context of the main thread, not in the context of the worker thread. So your loop is running in the main thread and is not letting hte main thread process new messages from its message queue. That is why your UI is not responding while your thread is running.

You need to restructure your thread to something more like this:

procedure TCustomThread.doUpdateUI;
begin
  ... do something that updates the UI here ...
end;

procedure TCustomThread.Execute;
begin
  while not Terminated do
  begin
    ... do something in the worker thread ...
    Synchronize(doUpdateUI);
    ... do something else in the worker thread ...
  end;
end;

var
  thrd : TCustomThread = nil;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if thrd = nil then
    thrd := TCustomThread.Create(False);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  if thrd <> nil then
  begin
    thrd.Terminate;
    thrd.WaitFor;
    FreeAndNil(thrd);
  end;
end;

Upvotes: 0

David Heffernan
David Heffernan

Reputation: 613013

The call to ProcessMessages is wrong and should be removed. As a broad and general rule, calls to ProcessMessages should be avoided. And this one serves no purpose at all.

The rest of your code simply runs a non-terminating loop. It does so using Synchronize which ensures that the non-terminating loop runs on the main thread. Hence the main thread is unable to service its message loop.

The entire purpose of threads is to be able to execute separate threads of execution. By using Synchronize you are running all your code in the main thread. Your code is equivalent to placing the non-terminating loop in the main thread.

You want to execute code in a different thread. So you should avoid the call to Synchronize. That should only be used for small, quick pieces of work that must execute on the main thread. Typically GUI updates.

Your execute method should be:

procedure TCustomThread.Execute;
begin
  while not Terminated do
  begin
    ....
  end;
end;

This introduces your loop, but the loop now executes in the thread. You can now add the useful code of your thread inside the body of the loop.

Remember that any use of VCL components must happen on the main thread. And that's where Synchronize is to be used.

Upvotes: 9

Related Questions