【问题标题】:Delphi : Sleep without freeze and processmessagesDelphi:没有冻结和处理消息的睡眠
【发布时间】:2015-06-24 12:22:58
【问题描述】:

我需要一种方法来暂停函数的执行几秒钟。我知道我可以使用 sleep 方法来做到这一点,但是这种方法在应用程序执行时会“冻结”应用程序。我也知道我可以使用下面的代码来避免冻结:

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

这种方法有两个问题:一个是冻结仍然每秒发生,第二个问题是每秒调用“application.processmessages”。我的应用程序是 CPU 密集型的,每个 processmessages 调用都会做很多不必要的工作,这些工作会占用不必要的 CPU 资源;我只想暂停工作流程,仅此而已。

我真正需要的是一种像 TTimer 一样暂停执行的方法,在下面的示例中:

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

这种方法的问题是 'myfunction' 不会等待 mytimer,它会在 mytimer 启用后立即运行。

还有其他方法可以实现我想要的暂停吗?

提前致谢。

【问题讨论】:

  • 所以我最好的选择是使用大量的 TTimer 和我想要的暂停?
  • “每个 processmessages 调用都会做很多不必要的工作”这句话让我有点困惑。 ProcessMessages 是不是类似于应用程序不冻结时所做的事情?因此,如果 not freeze 正在做 不必要的工作,这应该是首先要修复的区域。
  • 出于各种原因,我在各种应用程序中做过类似的事情。重要的是,这取决于您需要暂停它的原因 - 这将指导您可以使用什么方法来处理它。
  • @UweRaabe:不完全是。当您让消息循环正常工作时,主线程在没有消息要处理的时候进入高效睡眠状态。通过在循环中调用ProcessMessages(),主线程不再休眠,即使没有消息等待处理,轮询消息队列也有开销。
  • I need a way to pause the execution of a function for some seconds -- 为什么? 我们应该通过接受的答案来猜测吗?帮我们(和未来的访客)一个忙,并详细说明您的具体问题。谢谢:)

标签: windows delphi delphi-7 delphi-2007


【解决方案1】:

正如大卫所说,最好的选项是将工作转移到一个单独的线程中并完全停止阻塞主线程。但是,如果你必须阻塞主线程,那么至少你应该只在确实有消息等待处理时才调用ProcessMessages(),并让线程在其余时间休眠。您可以使用MsgWaitForMultipleObjects() 来处理它,例如:

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;

或者:

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);

【讨论】:

    【解决方案2】:

    将需要暂停的任务移到单独的线程中,以免干扰 UI。

    【讨论】:

    • 所以如果我有很多任务要暂停,我需要为每个任务一个线程吗?没办法随心所欲地使用睡眠?
    • 在 UI 线程中调用 Sleep,您将终止 UI。因此,不要在 UI 线程中调用 Sleep。因此,如果你想调用 Sleep 在另一个线程中进行。至于如何以最好的方式解决您的实际问题,我们无法完全看到这个问题。
    • 除此之外,您还需要了解,一旦线程暂停,它就无法执行其他任何操作。中途暂停一个函数,做其他事情,然后在没有线程的情况下恢复,可能意味着制作一个状态机。不是微不足道的。
    • 检查@remy-lebeau 的解决方案,它完全符合我的要求。
    • 我只是想暂停而不冻结 UI,也不需要调用 processmessages。我同意最好的选择是将任务移动到另一个线程,但是虽然我不能这样做,但工作量很大。 Remy 解决方案现在解决了这个问题,所以我有时间编辑代码并做正确的事情。
    【解决方案3】:

    值得怀疑的是,Application.ProcessMessages 真的会消耗太多的处理器时间。您可以尝试存储开始等待的时间,然后开始重复 Application.ProcessMessages 直到...;循环检查存储时间和当前时间之间的时间跨度。

    【讨论】:

    • 这是一个繁忙的循环,它将与处理器挂钩。更不用说 ProcessMessages 是邪恶的。
    【解决方案4】:

    如果您对使用计时器没有问题,您可以这样做:

    (ouside the timer-event:)
    mytimer.interval := 5000;
    mytimer.tag:=0;
    mytimer.enabled := true;
    
    (inside the timer-event:)
    mytimer.tag:=mytimer.tag+1;
    if mytimer.tag=2 then begin
      mytimer.enabled:=false;
      myfunction;
    end;
    

    【讨论】:

      猜你喜欢
      • 2022-01-05
      • 2012-05-25
      • 1970-01-01
      • 1970-01-01
      • 2011-05-18
      • 2018-06-12
      • 1970-01-01
      • 2012-03-04
      • 1970-01-01
      相关资源
      最近更新 更多