C ++函数在while(GetMessage(...)){...}

时间:2018-06-26 09:24:38

标签: c++ winapi while-loop sendmessage

我真的很荒谬。在我进行C ++编程时,需要等待很长时间。因此,我决定使用while(){...}

模式1::如果我使用while (1) {...}
CPU将承受最大的负载。 (它占用一个CPU的最大负载。)

模式2:

while(GetMessage(&msg, NULL, 0, 0)
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

将减轻CPU的负担。
但是,while(1){...}while(GetMessage(&msg, NULL, 0, 0)){...}之间的语句执行存在很大差异。

例如

模式1

dwEndTime = time(NULL) + 120;
while(1)
{
    if(IsOutOfTime())
        break;
}

模式2

dwEndTime = time(NULL) + 120;
while(GetMessage(&msg, NULL, 0, 0)
{
    if(IsOutOfTime())
        break;

    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

IsOutOfTime()的实现:

BOOL IsOutOfTime()
{
    if(time(NULL) >= dwEndTime)
        return TRUE;

    return FALSE;
}

导致不同的执行结果。 break模式2 下不起作用。

请有人帮我解决这个问题。

如果我在Sleep(...)中输入while (1) {...},则可以减少CPU负载,但是我讨厌这样的编码样式。

1。模式1 模式2 之间有哪些内在区别。
2。。我是否滥用了模式2
3。。如何不使用sleep来减少CPU负载。

5 个答案:

答案 0 :(得分:3)

如果可能,应避免同时使用模式1和2,因为两者都会轮询以检查您是否没时间。

模式1占用100%的cpu,不会处理任何Windows消息。
模式2仅在收到消息时检查超时,因此它可能会错过一段时间的超时。

相反,您应该研究使用timer的方法,该方法会在时间不足时触发。计时器到期后,GetMessage将返回一条WM_TIMER消息。 (我假设IsOutOfTime只是检查是否已经过去了一段时间。)

答案 1 :(得分:2)

WinAPI包含一些允许等待事件的函数,WaitForSingleObjectWaitForMultipleObject函数(以及它们的...Ex扩展名。这种等待不会给CPU造成压力,因为该进程是处于等待状态。

如果您有GUI程序并希望保持其响应速度,则应循环使用MsgWaitForMultipleObjects函数。如果您需要等待一个对象,它会给出:

DWORD cr
while ((cr = MsgWaitForMultipleObjects(1, &handle, FALSE, milli_sec_timeout, QS_ALLINPUT)) {
    if (cr == WAIT_OBJECT_0) {
        // event has been signaled process it
        break;
    }
    else if (cr == WAIT_OBJECT_0 + 1) {
        // something has arrived in the thread event loop: process the message
        GetMessage(&msg, NULL, 0, 0);
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    else if (cr == WAIT_TIMEOUT) {
        // timeout has elapsed
        ...
    }
    else { // abnormal condition, see doc for more
        ...
    }
}

答案 2 :(得分:2)

使线程等待给定时间段的最简单方法是使用Sleep()

Sleep(1000 * 120);

但是请注意,调用线程在睡眠时将被完全阻塞。如果在GUI线程中调用它,则GUI将被冻结,直到完成睡眠并且控制返回到GUI消息循环为止。

在这种情况下,您可以使用SetTimer()并让GUI消息循环根据需要处理WM_TIMER条消息。

另一种选择是改用Waitable Timer,例如:

HANDLE hTimer = CreateWaitableTimer(NULL, TRUE, NULL);

LARGE_INTEGER liDueTime;
liDueTime.QuadPart = -10000000 * 120;
SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, FALSE);

// wait for hTimer to be signaled...

CloseHandle(hTimer);

在GUI线程中,您可以使用MsgWaitForMultipleObjects()等待计时器发出信号,同时仍在维护GUI消息,例如:

HANDLE hTimer = CreateWaitableTimer(NULL, TRUE, NULL);

LARGE_INTEGER liDueTime;
liDueTime.QuadPart = -10000000 * 120;
SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, FALSE);

bool keepLooping = true;
do
{
    DWORD ret = MsgWaitForMultipleObjects(1, &hTimer, FALSE, INFINITE, QS_ALLINPUT);
    if (ret == WAIT_OBJECT_0)
    {
        // timer elapsed ...
        break;
    }

    if (ret == (WAIT_OBJECT_0 + 1))
    {
        MSG msg;
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
            {
                PostQuitMessage(msg.wParam);
                keepLooping = false;
                break;
            }

            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
}
while (keepLooping);

CloseHandle(hTimer);

如果在等待计时器时不需要处理GUI消息,则可以改用WaitForSingleObject()WaitForMultipleObjects()(取决于您是仅等待计时器,还是取决于计时器)也要等待其他对象):

HANDLE hTimer = CreateWaitableTimer(NULL, TRUE, NULL);

LARGE_INTEGER liDueTime;
liDueTime.QuadPart = -10000000 * 120;
SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, FALSE);

WaitForSingleObject(hTimer, INFINITE);

CloseHandle(hTimer);

答案 3 :(得分:1)

好的,因为您似乎在SetTimer()(这似乎是这里的最佳解决方案)中苦苦挣扎,所以我将为您发布完整的源代码。真的很简单,请不要再忙于忙循环了。

注意:此代码不需要窗口,但确实需要一个事件循环(但您似乎对此没问题)。

设置:

VOID CALLBACK MyTimerProc (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
DWORD dwTimerID = SetTimer (NULL, 0, 120 * 1000, MyTimerProc);  // 2 minute timeout

消息循环(请勿在此处测试超时)

MSG msg;
while (GetMessage (&msg, NULL, 0, 0)
{
    TranslateMessage (&msg);
    DispatchMessage (&msg);
}

计时器进程:

VOID CALLBACK MyTimerProc (HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
    KillTimer (NULL, dwTimerID);
    // Do whatever it is you wanted to do when your timeout elapsed
}

答案 4 :(得分:0)

我解决了我的问题。 while(GetMessage(..)){..}已阻止
我在GetMessage()中严重使用了while(...)
因为,如果没有消息要翻译和发送,则GetMessage将被阻止,直到发生任何消息。
因此,我实现了这样的代码。

#define MESSAGE_NOT_OUT_OF_TIME WM_USER+1
#define MESSAGE_OUT_OF_TIME WM_USER+2

PostMessage(NULL, MESSAGE_NOT_OUT_OF_TIME, 0, 0);

while(GetMessage(&msg, NULL, 0, 0)
{
    if (MESSAGE_NOT_OUT_OF_TIME == msg.message)
        IsOutofTime();

    if (MESSAGE_OUT_OF_TIME == msg.message)
        break;

    TranslateMessage(&msg);
    DispatchMessage(&msg);
}

IsOutofTime()的实现

VOID IsOutofTime()
{
    if(dwEndTime <= time(NULL))
        PostMessage(NULL, MESSAGE_OUT_OF_TIME, 0, 0);
    else
        PostMessage(NULL, MESSAGE_NOT_OUT_OF_TIME, 0, 0);
}

尽管CPU的负载约为70%到80%,但我已经找到原因并解决了问题。