如何用 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 线程的行为。
当然,不言而喻的是,一旦引入线程,就会引入难以诊断的潜在错误,例如竞争条件和死锁。如果 func1
、func2
或 funcN
修改全局状态,您将需要通过临界区或其他一些同步方法来同步访问。
我有一个应用程序需要按部就班地连续执行某些操作,如下所示:
...
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 线程的行为。
当然,不言而喻的是,一旦引入线程,就会引入难以诊断的潜在错误,例如竞争条件和死锁。如果 func1
、func2
或 funcN
修改全局状态,您将需要通过临界区或其他一些同步方法来同步访问。