Delphi : Sleep without freeze and processmessages

13,230

Solution 1

As David stated, the best option is to move the work into a separate thread and stop blocking the main thread altogether. But, if you must block the main thread, then at the very least you should only call ProcessMessages() when there really are messages waiting to be processed, and let the thread sleep the rest of the time. You can use MsgWaitForMultipleObjects() to handle that, eg:

var
  Start, Elapsed: DWORD;

// sleep for 5 seconds without freezing 
Start := GetTickCount;
Elapsed := 0;
repeat
  // (WAIT_OBJECT_0+nCount) is returned when a message is in the queue.
  // WAIT_TIMEOUT is returned when the timeout elapses.
  if MsgWaitForMultipleObjects(0, Pointer(nil)^, FALSE, 5000-Elapsed, QS_ALLINPUT) <> WAIT_OBJECT_0 then Break;
  Application.ProcessMessages;
  Elapsed := GetTickCount - Start;
until Elapsed >= 5000;

Alternatively:

var
  Ret: DWORD;
  WaitTime: TLargeInteger;
  Timer: THandle;

// sleep for 5 seconds without freezing 
Timer := CreateWaitableTimer(nil, TRUE, nil);
WaitTime := -50000000; // 5 seconds
SetWaitableTimer(Timer, WaitTime, 0, nil, nil, FALSE);
repeat
  // (WAIT_OBJECT_0+0) is returned when the timer is signaled.
  // (WAIT_OBJECT_0+1) is returned when a message is in the queue.
  Ret := MsgWaitForMultipleObjects(1, Timer, FALSE, INFINITE, QS_ALLINPUT);
  if Ret <> (WAIT_OBJECT_0+1) then Break;
  Application.ProcessMessages;
until False;
if Ret <> WAIT_OBJECT_0 then
  CancelWaitableTimer(Timer);
CloseHandle(Timer);

Solution 2

Move the task that needs to be paused into a separate thread so that it does not interfere with the UI.

Solution 3

It is rather doubtful, that Application.ProcessMessages will really consumpt too much processor time. You can try to store the moment of time when you start waiting and then begin a repeat Application.ProcessMessages until...; circle checking the time span between the stored and current time.

Share:
13,230

Related videos on Youtube

delphirules
Author by

delphirules

Delphi is dead but still rocks.

Updated on June 04, 2022

Comments

  • delphirules
    delphirules almost 2 years

    I need a way to pause the execution of a function for some seconds. I know i can use the sleep method to do it, but this method 'freezes' the application while its execution. I also know i can use something like the code below to avoid freezing :

    // sleeps for 5 seconds without freezing 
    for i := 1 to 5 do
        begin
        sleep(1000);
        application.processmessages;
        end;
    

    There are two problems of this approach : one is the fact the freezing still occurs each one second and the second problem is the calling to 'application.processmessages' each second. My app is CPU intensive and each processmessages call do a lot of unnecessary work that uses unnecessary CPU power ; i just want to pause the workflow, nothing more.

    What i really need would be a way to pause the execution just like a TTimer, in the example below :

       // sleeps for 5 seconds
       mytimer.interval := 5000;
       mytimer.enabled := true;
       // wait the timer executes
       // then continue the flow
       // running myfunction
       myfunction;
    

    The problem of this approach is 'myfunction' won't wait the for mytimer, it will run right after the mytimer is enabled.

    Is there another approach to achieve a pause like i want ?

    Thanks in advance.

    • MikeD
      MikeD
      I've done things like this in various apps for various reasons. The big thing is that it depends on why you need to pause it - that will direct what method you can use to handle it.
    • Wolf
      Wolf
      I need a way to pause the execution of a function for some seconds -- Why? Should we guess it by means of the accepted answer? Do us (and future visitors) a favour and tell a bit more about your specific problem. Thanks :)
  • delphirules
    delphirules almost 9 years
    So if i have a lot of tasks to be paused, i need one thread for each ? No way to use sleep as i want ?
  • David Heffernan
    David Heffernan almost 9 years
    Call Sleep in the UI thread and you'll kill the UI. Ergo, don't call Sleep in the UI thread. Ergo, if you want to call Sleep do it in another thread. As for how to solve your actual problem in the best way, we can't see that problem fully.
  • David Heffernan
    David Heffernan almost 9 years
    Beyond all of that you need to appreciate that once a thread pauses it cannot do anything else. To pause a function part way through, do other stuff, and then resume, without threads, probably means making a state machine. Not trivial.
  • David Heffernan
    David Heffernan almost 9 years
    That's a busy loop which will peg a processor. Not to mention ProcessMessages being evil.
  • delphirules
    delphirules almost 9 years
    Check the solution of @remy-lebeau, it does exactly what i was looking for.
  • delphirules
    delphirules almost 9 years
    I just wanted to pause without freeze the UI, and without calling processmessages unnecessary. I agree the best option is to move the task to another thread, but for while i can't do it, would be a lot of workk. Remy solution solved the issue for now, so i have time to edit the code and do the right thing.