如何在长循环中处理消息时最小化开销

时间:2010-08-04 05:21:38

标签: delphi optimization overhead

我的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是唯一的方法。

5 个答案:

答案 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,以使进度条重新绘制。

如果应用程序可以继续,至少有一段时间,那么将代码放在一个线程中是个好主意。

无论如何将循环代码放在一个线程中可能是合理的,特别是如果你想做可能中止处理的事情。如果你以前从未使用过线程,那么就会有一些学习曲线,但对于像你描述的简单循环,网上有很多例子。