在长循环中处理消息时如何最大限度地减少开销

发布于 2024-09-12 22:59:11 字数 629 浏览 1 评论 0原文

我的 Delphi 程序中有一些很长但简单的循环,可能会循环数百万次并需要几秒钟的时间来执行。循环内部的代码非常快并且已经过优化。只是需要很长时间,因为它已经做了很多次了。

例如:

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

I've got some long but simple loops in my Delphi program that may loop millions of times and take some seconds to execute. The code inside of the loop is very fast and has been optimized. It just takes long because it is done so many times.

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;

Now I don't want my program to be non-responsive, so I want to add an Application.ProcessMessages inside the loop. But I also want the added statements to slow down my loop as little as possible.

I am following a linked list, so I don't even have a counting variable available and would have to add one if I wanted intervals. Or I'd have to add a timer, but need to minimize the time checking.

How should I implement this to minimize the overhead that's added?


Conclusion:

For now, I'm doing something like APZ28's answer.

But it looks like long term I should implement some sort of threading to handle this. Thanks for pointing this out, because I thought that Application.ProcessMessages was the only way to do it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

丑丑阿 2024-09-19 22:59:11

您能否将工作循环放入一个线程中,从而释放主线程以进行 GUI 循环处理。

Can you put the work loop in a thread, freeing up the main thread for the GUI loop processing.

深海不蓝 2024-09-19 22:59:11

将其放在线程下并不简单,因为它需要锁共享资源(如果有)。一个好技巧是有一个计数器,在处理循环后,调用 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;

Put it under thread is not trival since it requires lock share resource if any. A good trick is having a counter, after processing # of loop, call 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;
殤城〤 2024-09-19 22:59:11

我还会投票给 Anreas 的主题或诸如 AsyncCalls 之类的内容。要禁止用户在需要的时间内执行任何不允许的操作,您可以在例程开始时设置一个标志,并在例程结束时重置它(无论如何您都必须更新 Screen.Cursor)。主线程可以检查此标志并在其 OnUpdate 事件中禁用所有受影响的操作。

I would also vote for a thread or something like Anreas' AsyncCalls. To prohibit the user doing any non-allowed actions during the time needed, you can set a flag when the routine starts and reset it when it ends (you have to update Screen.Cursor anyway). The main thread can check this flag and disable all affected actions in their OnUpdate event.

柠檬 2024-09-19 22:59:11

最好的选择是将循环移至其自己的工作线程中,这样主线程就不会被阻塞,然后您根本不需要调用 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; 

The best option is to move the loop into its own worker thread so the main thread is not blocked, then you do not need to call ProcessMessages() at all.

However, if you must do the loop in the main thread, then you can use MsgWaitForMultipleObject() to detect when to call ProcessMessages(), ie:

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; 

Alternatively with 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; 

Alternatively with 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; 
秋心╮凉 2024-09-19 22:59:11

需要决定的一个问题是,在获得循环正在计算的答案之前,您的应用程序是否可以继续。如果不能,那么应用程序“响应式”就没有多大意义。如果您尝试更新进度条或其他内容,则可以每隔一定次数的迭代对包含进度条的控件调用 .Repaint ,以使进度条重新绘制。

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

无论如何,将循环代码放入线程中可能是合理的,特别是如果您想要执行可能中止处理之类的操作。如果您以前从未使用过线程,那么会有一些学习曲线,但是对于像您描述的简单循环,网络上有很多示例。

One question to decide is whether or not your application can continue before you have the answer to the whatever the loop is calculating. If it cannot, then there is not much point in the application being "responsive". If you are trying to update a progress bar or something, you can call .Repaint on the control containing the progress bar every certain number of iterations to cause the progress bar to repaint.

If the application can continue, at least for a while, then putting the code in a thread is a good idea.

Putting the looping code in a thread is probably reasonable anyhow, especially if you want to do things like possibly abort the processing. If you have never used threads before, there is a bit of a learning curve, but for a simple loop like you describe there are lots of examples on the web.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文