如何用 SetTimer 代替 Sleep()?

How to use SetTimer replace Sleep()?

我有一个应用程序需要按部就班地连续执行某些操作,如下所示:

...
func1();
Sleep(2000);
func2();
Sleep(3000);
func3();
...

我们知道,调用 Sleep() 会导致用户界面 "freeze",因此例如在尝试移动应用程序时没有响应 window。

因此,我尝试改用 SetTimer(),因为它似乎不会导致 UI 冻结,但我的问题是如何实现与 Sleep 相同的 "waiting" 功能() 何时使用 SetTimer()?

...
func1();
SetTimer(hwnd, ID_Timer2s,2000, Timerfunc2s);// how to make sure 2seconds waiting happen between func1 and func2?
func2();
SetTimer(hwnd, ID_Timer3s,3000, Timerfunc3s);
func3();
...

// my intention is kill the timer once timeout, then go back next func2(), how?
VOID CALLBACK Timerfunc2s( 
    HWND hwnd,        
    UINT message,
    UINT idTimer,
    DWORD dwTime)
{ 
    KillTimer(hwnd, ID_Timer2s);
} 

VOID CALLBACK Timerfunc3s( 
    HWND hwnd,        
    UINT message,
    UINT idTimer,
    DWORD dwTime)
{ 
    KillTimer(hwnd, ID_Timer3s);
} 

感谢您的任何建议。

你想 运行 func2() 只有在 ID_Timer2s 过期后,所以 运行 它在 ID_Timer2s 过期后。不要马上运行。

...
func1();
SetTimer(hwnd, ID_Timer2s,2000, Timerfunc2s);
...

VOID CALLBACK Timerfunc2s( 
    HWND hwnd,        
    UINT message,
    UINT idTimer,
    DWORD dwTime)
{ 
    KillTimer(hwnd, ID_Timer2s);
    func2();
    SetTimer(hwnd, ID_Timer3s,3000, Timerfunc3s);
} 

VOID CALLBACK Timerfunc3s( 
    HWND hwnd,        
    UINT message,
    UINT idTimer,
    DWORD dwTime)
{ 
    KillTimer(hwnd, ID_Timer3s);
    func3();
} 

正如评论中所指出的,这通常不是适合 GUI 应用程序的设计。

但是,它可以适用于一些非常简单的应用程序,例如,那些执行不需要用户交互的任务的应用程序,其 GUI 只提供反馈到任务进度。

在这种情况下,并假设代码未在 window 过程 的上下文中调用,这样的事情应该有效:

...
func1();
MySleep(hwnd, 2000);
func2();
MySleep(hwnd, 3000);
func3();
...

BOOL MySleepTimerFlag;

void MySleep(HWND hwnd, DWORD timeout)
{
   MySleepTimerFlag = FALSE;
   SetTimer(hwnd, ID_MySleepTimer, timeout, MySleepTimerFunc);
   while (MySleepTimerFlag == FALSE)
   {
      if (!GetMessage(&Msg, NULL, 0, 0)) fail();
      TranslateMessage(&Msg);
      DispatchMessage(&Msg);
   }
}

VOID CALLBACK MySleepTimerFunc( 
    HWND hwnd,        
    UINT message,
    UINT idTimer,
    DWORD dwTime)
{ 
    MySleepTimerFlag = TRUE;
    KillTimer(hwnd, ID_MySleepTimer);
} 

请注意,这段代码是即兴写的;我没有尝试编译它,更不用说测试它了。但它至少应该给你一个想法。

此外,请注意,如果它让您不高兴,您可以可以 消除全局 - 附加一个指针作为 window 属性 或类似的 - 但同样,在非常简单的应用程序中使用时,全局变量并没有那么糟糕。 :-)

最后,对 MySleep() 的调用不得在 window 过程的上下文中的条件 很重要 。这意味着上面代码的第一部分不能通过选择菜单项等方式触发。如果是,那么您必须改用 Raymond 的回答中概述的方法,或者按照 David 的建议创建一个新线程。

...此外,正如 Raymond 指出的那样,您必须注意 GUI 没有任何模态元素,例如菜单或对话框,或者至少在这段代码 运行.

有(至少)3 个选项可以满足您的需求。第一个,使用计时器,陈百强覆盖。第二,使用定制的等待函数,Harry Johnston 覆盖了。第三个选项是创建一个线程,并使用该线程完成工作。

例如:

DWORD WINAPI ThreadProc(LPVOID lpThreadParameter)
{
    func1();
    Sleep(2000);
    func2();
    Sleep(3000);
    func3();
    ...
    return 0;
}

...

DWORD threadId = 0;
HANDLE hThread = CreateThread(NULL, 255, &ThreadProc, NULL, 0, &threadId);

当您需要与 UI 交互时,麻烦就来了。您不能直接从备用线程对 UI 执行操作,但是您可以使用的一种技术是 post messages 到您的 UI and/or 使用共享状态变量来影响UI 线程的行为。

当然,不言而喻的是,一旦引入线程,就会引入难以诊断的潜在错误,例如竞争条件和死锁。如果 func1func2funcN 修改全局状态,您将需要通过临界区或其他一些同步方法来同步访问。