Main thread blocking parallel thread?

816 views Asked by At

Create a VCL Forms Application, put a TButton and a TMemo on the Form, and write this code in the button's OnClick handler:

uses
  OtlParallel, OtlTaskControl;

procedure TForm2.btnStartLoopClick(Sender: TObject);
var
  starttime: Cardinal;
  k: Integer;
begin
  mmoTest.Lines.Clear;
  for k := 1 to 50 do
    mmoTest.Lines.Add('Line ' + IntToStr(k));

  starttime := GetTickCount;
  Parallel.Async(
    procedure
    var
      i: Integer;
    begin
      for i := 1 to 50 do
      begin
        Sleep(100);
        mmoTest.Lines[i - 1] := mmoTest.Lines[i - 1] + FormatDateTime(' nn:ss:zzz', Now);
      end;
    end,
    Parallel.TaskConfig.SetPriority(TOTLThreadPriority.tpHighest).OnTerminated(
    procedure
    begin
      mmoTest.Lines.Add(IntToStr(GetTickCount - starttime) + ' milliseconds');
    end));
end;

Now run the program and make this test:

  1. Click on the button, simply wait for the loop to complete and look at the time displayed in the last line of the memo: It should be approximately 5300 milliseconds.

  2. Now click again on the button, click and hold the form's title bar and move the form around quickly until the loop has finished. Now look again at the memo's last line: In my tests, the time was over 7000 milliseconds. Obviously, the main thread is blocking the parallel thread!

So how can the main thread blocking the parallel thread be avoided?

1

There are 1 answers

7
Remy Lebeau On BEST ANSWER

First, this code is not thread-safe, as the asynchronous code is directly accessing the TMemo from a task thread outside of the main UI thread. You cannot do that. A worker thread MUST synchronize with the UI thread in order to access UI controls safely or else bad things can happen. You can use TThread.Synchronize(), TThread.Queue(), or IOmniTask.Invoke() for that synchronization.

Second, while you are holding down the mouse on the title bar, the main UI message loop is blocked (a separate modal message loop is being run by the OS until you let go of the mouse). As such, the task's OnTerminate event handler may not be run until the main message loop regains control. That would account for why your timer duration is reportedly longer than expected, not because the task loop was blocked.

Third, Sleep() is not absolute. It will sleep for at least the requested amount of time, but may sleep for longer. So your task loop will run for at least 5 seconds, but may be a little longer.