主线程阻塞并行线程?

use*_*348 3 delphi multithreading omnithreadlibrary delphi-10.1-berlin

创建一个VCL表单应用程序,在表单上放置一个TButton和一个TMemo,并在按钮的OnClick处理程序中编写此代码:

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;
Run Code Online (Sandbox Code Playgroud)

现在运行程序并进行此测试:

  1. 单击按钮,只需等待循环完成并查看备忘录最后一行中显示的时间:它应该是大约5300毫秒.

  2. 现在再次单击按钮,单击并按住表单的标题栏并快速移动表单,直到循环结束.现在再看一下备忘录的最后一行:在我的测试中,时间超过7000毫秒.显然,主线程是阻塞并行线程!

那么如何避免阻塞并行线程的主线程呢?

Rem*_*eau 10

首先,此代码不是线程安全的,因为异步代码直接TMemo从主UI线程之外的任务线程访问.你不能这样做.工作线程必须与UI线程同步才能安全地访问UI控件,否则可能会发生不良事件.您可以使用TThread.Synchronize(),, TThread.Queue()或IOmniTask.Invoke()进行同步.

其次,当您在标题栏上按住鼠标时,主UI消息循环被阻止(操作系统正在运行单独的模态消息循环,直到您放开鼠标为止).因此,在OnTerminate主消息循环重新获得控制之前,可能不会运行任务的事件处理程序.这将解释为什么您的计时器持续时间据报道比预期更长,这不是因为任务循环被阻止.

第三,Sleep()不是绝对的.它将至少睡眠所需的时间,但可能会睡得更久.所以你的任务循环将运行至少 5秒,但可能会更长一点.

  • @ user1580348:否.**任何**访问,读取或写入,必须同步.主要原因是因为`TWinControl.Handle`属性不是线程安全的,它可能会在单独的线程使用`HWND`时改变值,这会导致各种问题.因此,即使读取与"HWND"相关的数据也必须受到保护. (5认同)