How can I minimize the overhead of processing messages in a long loop

I have several long but simple loops in my Delphi program that can take millions of times and take a few seconds. The code inside the loop is very fast and optimized. It will take a lot of time because it is done many times.

eg:.

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 immune, so I want to add Application.ProcessMessages inside the loop. But I also want the added statements to slow my cycle as little as possible.

I follow a linked list, so I don’t even have a count variable, and I would need to add one if I needed intervals. Or I need to add a timer, but you need to minimize time checking.

How to implement this in order to minimize the overhead that has been added?


Conclusion:

At the moment, I'm doing something like APZ28's answer.

But it seems that for a long time I have to implement some kind of thread to cope with this. Thanks for pointing this out because I thought Application.ProcessMessages was the only way to do this.

+4
source share
5 answers

Can you put a work loop in a thread, freeing up the main thread to handle the GUI loop.

+8
source

Putting it in a stream is not trivial, since it requires a shared resource, if any. A good trick has a counter, after processing the # 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; 
+4
source

I will also vote for a thread or something like Anreas AsyncCalls . To prevent the user from doing any illegal actions for the required time, you can set the flag when the program starts and reset when it finishes (you still need to update Screen.Cursor). The main thread can check this flag and disable all affected actions in their OnUpdate event.

+2
source

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

However, if you need to loop through the main thread, you can use MsgWaitForMultipleObject () to determine when to call ProcessMessages (), i.e.:

 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; 
+2
source

The question is whether your application can continue to work before you answer what the loop calculates. If this does not succeed, then the application does not make much sense to β€œrespond”. If you are trying to update the progress bar or something else, you can call .Repaint on the control that contains the progress bar, each specific number of iterations to cause the progress bar to display.

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

Putting loop code in a thread is probably smart, especially if you want to do things like maybe interrupt processing. If you've never used streams before, there is a bit of a learning curve, but for a simple loop, as you described, there are many examples on the Internet.

+1
source

All Articles