我的Delphi程序中有一些很长但很简单的循环,它可能循环数百万次并需要几秒钟才能执行。循环内部的代码非常快并且已经过优化。这需要很长时间,因为它已经完成了很多次。
e.g:
Screen.Cursor = crHourGlass;
R := FirstRecord;
while R <> nil do begin
{ do something simple with R.Value }
R := R.NextRecord;
end;
Screen.Cursor := crDefault;
现在我不希望我的程序无响应,所以我想在循环中添加一个Application.ProcessMessages。但我也希望增加的语句尽可能少地减慢我的循环。
我正在关注一个链表,所以我甚至没有可用的计数变量,如果我想要间隔,则必须添加一个。或者我必须添加一个计时器,但需要最小化检查时间。
我应该如何实现这一点以最大限度地减少添加的开销?
结论:
现在,我正在做类似APZ28的回答。
但看起来长期我应该实现某种线程来处理这个问题。感谢您指出这一点,因为我认为Application.ProcessMessages是唯一的方法。
答案 0 :(得分:8)
您可以将工作循环放在一个线程中,释放主线程以进行GUI循环处理。
答案 1 :(得分:4)
将它放在线程下是不是因为它需要锁共享资源(如果有的话)。一个好的技巧是在处理#循环后调用ProcessMessages
var
LoopCounter: Integer;
LoopCounter := 0;
R := FirstRecord;
while R <> nil do begin
Inc(LoopCounter);
if (LoopCounter >= ???) then
begin
LoopCounter := 0;
Application.ProcessMessages;
end;
{ do something simple with R.Value }
R := R.NextRecord;
end;
答案 2 :(得分:2)
我也会投票给Anreas'AsyncCalls之类的线程。要禁止用户在所需的时间内执行任何不允许的操作,您可以在例程启动时设置一个标志,并在结束时重置它(无论如何都必须更新Screen.Cursor)。主线程可以检查此标志并禁用其OnUpdate事件中的所有受影响的操作。
答案 3 :(得分:2)
最好的选择是将循环移动到自己的工作线程中,这样主线程就不会被阻塞,那么你根本不需要调用ProcessMessages()。
但是,如果必须在主线程中执行循环,则可以使用MsgWaitForMultipleObject()来检测何时调用ProcessMessages(),即:
Screen.Cursor = crHourGlass;
R := FirstRecord;
while R <> nil do begin
{ do something simple with R.Value }
if MsgWaitForMultipleObjects(0, nil, False, 0, QS_ALLINPUT) = WAIT_OBJECT_0 then
Application.ProcessMessages;
R := R.NextRecord;
end;
Screen.Cursor := crDefault;
或者使用PeekMessage():
var Msg: TMsg;
Screen.Cursor = crHourGlass;
R := FirstRecord;
while R <> nil do begin
{ do something simple with R.Value }
if PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE) then
Application.ProcessMessages;
R := R.NextRecord;
end;
Screen.Cursor := crDefault;
或者使用GetQueueStatus():
Screen.Cursor = crHourGlass;
R := FirstRecord;
while R <> nil do begin
{ do something simple with R.Value }
if GetQueueStatus(QS_ALLINPUT) <> 0 then
Application.ProcessMessages;
R := R.NextRecord;
end;
Screen.Cursor := crDefault;
答案 4 :(得分:1)
要确定的一个问题是,在您获得循环计算的答案之前,您的应用程序是否可以继续。如果它不能,那么应用程序中的“响应”没有多大意义。如果您尝试更新进度条或其他内容,可以在包含进度条的控件上每隔一定的迭代次数调用.Repaint,以使进度条重新绘制。
如果应用程序可以继续,至少有一段时间,那么将代码放在一个线程中是个好主意。
无论如何将循环代码放在一个线程中可能是合理的,特别是如果你想做可能中止处理的事情。如果你以前从未使用过线程,那么就会有一些学习曲线,但对于像你描述的简单循环,网上有很多例子。