创建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;
现在运行该程序并进行此测试:
点击按钮,只需等待循环完成,然后查看备忘录最后一行显示的时间:它应该是大约5300毫秒。
现在再次单击按钮,单击并按住表单的标题栏并快速移动表单,直到循环结束。现在再看一下备忘录的最后一行:在我的测试中,时间超过7000毫秒。显然,主线程是阻塞并行线程!
那么如何避免阻塞并行线程的主线程?
答案 0 :(得分:10)
首先,这段代码不是线程安全的,因为异步代码直接从主UI线程之外的任务线程访问TMemo
。你不能这样做。工作线程必须与UI线程同步才能安全地访问UI控件,否则可能会发生不良事件。您可以使用TThread.Synchronize()
,TThread.Queue()
或IOmniTask.Invoke()
进行同步。
其次,当您在标题栏上按住鼠标时,主UI消息循环被阻止(操作系统正在运行单独的模态消息循环,直到您放开鼠标为止)。因此,在主消息循环重新获得控制之前,任务的OnTerminate
事件处理程序可能无法运行。这将解释为什么您的计时器持续时间据报道比预期更长,而不是因为任务循环被阻止。
第三,Sleep()
不是绝对的。它会在至少的所需时间内休眠,但可能会睡得更久。因此,您的任务循环将至少 5秒运行,但可能会更长一点。