鼠标抖动/消息处理循环

时间:2009-10-21 09:08:19

标签: c++ multithreading windows-xp

我编写了一个多线程程序,它会做一些思考并在此过程中打印出一些诊断信息。我注意到如果我在程序运行时摇晃鼠标,那么程序运行得更快。现在我可以在这里详细介绍我是如何打印的...但是我暂时还会推迟,因为我注意到在许多其他程序中,如果鼠标摇晃,事情会发生得更快,我想知道是否有是一些经典的错误,许多人已经做出了消息循环以某种方式减慢了非移动鼠标的速度。

编辑:我的“打印”方法如下......我有一个丰富的编辑控制窗口来显示文本。当我想打印某些内容时,我将新文本附加到窗口中的现有文本,然后使用SendMessage(,WM_PAINT,0,0)重绘窗口。

实际上它有点复杂,我有多个丰富的编辑控制窗口,每个线程一个(我的4核PC上有4个线程)。我的“my_printf()”的大致轮廓如下:

void _cdecl my_printf(char *the_text_to_add)
{
    EnterCriticalSection(&my_printf_critsec);
    GetWindowText(...); // get the existing text
    SetWindowText(...); // append the_text_to_add
    SendMessage(...WM_PAINT...);
    LeaveCriticalSection(&my_printf_critsec);
}

我应该指出,我已经在非多线程程序中使用这种打印方法多年,甚至没有注意到与鼠标抖动的任何交互。

编辑:好的,这是我的整个messageloop,它在子线程上运行,而子线程完成它们的工作。子线程调用my_printf()来报告它们的进度。

for(;;)
{
    DWORD   dwWake;
    MSG msg;

    dwWake = MsgWaitForMultipleObjects(
                            current_size_of_handle_list,
                            hThrd,
                            FALSE,
                            INFINITE,
                            QS_ALLEVENTS);

    if (dwWake >= WAIT_OBJECT_0 && dwWake < (WAIT_OBJECT_0 + current_size_of_handle_list))
    {
        int index;
        index = dwWake - WAIT_OBJECT_0;
        int j;
        for (j = index+1;j < current_size_of_handle_list;j++)
        {
            hThrd[j-1] = hThrd[j];
        }
        current_size_of_handle_list--;
        if (current_size_of_handle_list == 0)
        {
            break;
        }

    }
    else if (dwWake == (WAIT_OBJECT_0 + current_size_of_handle_list))
    {
        while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    else if (dwWake == WAIT_TIMEOUT)
    {
        printmessage("TIMEOUT!");
    }
    else
    {
        printmessage("Goof!");
    }
}
编辑:解决了! 这可能是一个丑陋的解决方案 - 但我只是将超时从无限改为20ms,然后在if(dwWake == WAIT_TIMEOUT)部分我交换了printmessage(“TIMEOUT!”);为:

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

我还没有关闭这个问题,因为我仍然想知道为什么原始代码本身并不起作用。

8 个答案:

答案 0 :(得分:3)

我在这里可以看到3个问题:

  1. WM_PAINT的文档说:The WM_PAINT message is generated by the system and should not be sent by an application.遗憾的是我不知道任何解决方法,但我认为SetWindowText()将负责重新绘制窗口,因此这个调用可能没用

  2. SendMessage()是一个阻塞调用,在应用程序处理完该消息之前不会返回。由于绘画可能需要一段时间才能处理,你的程序很可能会在你的关键部分被绞死,特别是在考虑我的第三点时。 PostMessage()在这里要好得多,因为你没有理由“现在”需要重新绘制你的窗口。

  3. 您在MsgWaitForMultipleObjects()中使用QS_ALLEVENTS,但此掩码不包含QS_SENDMESSAGE标志。因此,您的SendMessage()调用可能会被忽略,并且不会唤醒您的线程。你应该使用QS_ALLINPUT

  4. 您可以使用INFINITE超时检查应用程序的行为吗?包括上述3个修改?

答案 1 :(得分:1)

好吧,我不能完全帮助你,因为我们没有足够的信息,但是我遇到了类似的问题,除非我移动鼠标或者经过一些(不是无意义的)延迟,否则我的应用程序不会刷新。

在调查问题时,我发现基本上,如果没有更多要处理的消息,GUI线程将会休眠。摇动鼠标将创建新的窗口消息发送到窗口,从睡眠中唤醒线程。

我的问题是我在OnIdle(MFC,不确定你)功能中进行处理,并且在进行一次处理后,线程将进入休眠状态。

我认为这不是你的问题,因为你似乎从你的帖子发布了一个Windows消息(WM_PAINT),我在我的情况下没有做什么(这应该唤醒gui线程)但是这可能有帮助你找到了解决问题的正确方向吗?

编辑:我虽然知道了一点,也许WM_PAINT有一个特例(就像你忘记调用Invalidate或其他东西,我不是Windows编程的专家)所以也许尝试发布另一条消息,如WM_USER你的应用程序,看看它是否解决了你的问题(这应该肯定唤醒我认为的gui线程)。同时发布对SendMessage函数的完整调用可能会有所帮助。

Edit2:好吧,看到你对Kelly French的评论之后,你似乎有完全相同的症状,所以我猜想,无论出于何种原因,你对PostMessage的调用似乎没有唤醒gui线程或其他东西类似。你为PostMessage的第一个参数传递了什么?我在我的案例中做的是用我的应用程序调用带有参数WM_USER,0,0的PostMessage。您还可以尝试PostThreadMessage变体,同时将主线程的当前threadID保存在变量中(请参阅GetCurrentThreadId)。

您也可以尝试在对象上调用Invalidate。 Windows会记住一个对象是否需要重新绘制,如果不需要则不会这样做。我不知道是否直接调用WM_PAINT会覆盖这个。

那就是我能想到的全部。至少你找到了修复,即使它不是最优雅的。

答案 2 :(得分:1)

如果我没记错的话,WM_PAINT是一个非常低优先级的消息,只有当消息队列为空时才会被中继。此外,Windows会将多个WM_PAINT消息合并为一个。我可以看到鼠标移动导致更少的重绘事件,每个事件处理更大的更新,从而提高性能。

答案 3 :(得分:0)

你完全确定该程序真的运行得更快吗?或者是更频繁刷新的输出?

答案 4 :(得分:0)

您使用的是SendMessage还是PostMessage?我很好奇,如果切换到另一个会让这些东西在这个特定的环境中变得“更好”。

取自developerfusion:

  

还有另一个类似的API   与SendMessage和。完全一样   那就是PostMessage API。两者都需要   相同的参数,但有一点点   区别。发送消息时   一个带有SendMessage窗口的窗口   调用过程和调用   程序(或线程)等待   要处理和回复的消息   回来,直到那时的召唤   程序没有恢复它   处理。有一点是错的   然而,这种方法,如果   正在忙着执行的程序   说明或程序   被挂了,因此没有时间回应   消息将反过来挂你的   程序也是因为你的程序会   等待可能永远不会的答复   到达。解决方案是使用   PostMessage而不是SendMessage。   PostMessage另一方面返回   立即到调用程序   无需等待线程   处理消息,从而节省您的   悬挂的程序。其中哪一个   你必须使用取决于你的   要求。

答案 5 :(得分:0)

您的GUI窗口是否已最大化?是否在您的应用程序窗口或其他应用程序或桌面等其他窗口上发生鼠标移动是否会发生?当鼠标移动到您的应用程序上时,mouse_move消息将被发送到您的消息队列。这可能会唤醒线程或强制WM_PAINT消息。

我怀疑打印实际上是否更快。我怀疑鼠标移动导致的消息数量增加会导致更多的窗口失效事件,因此文本更新会更加细化。当鼠标没有被移动时,打印是否发生在较大的块中,比如一次20个字符的块和5个字符的块?

你能否通过加快印刷来澄清你的意思?是绝对的,比如每分钟100个字符,每分钟20个字符?或者它更像是每分钟100个字符,但是当鼠标静止时它们会以块的形式出现?

答案 6 :(得分:0)

我认为这与前景和后台处理有关。如果操作系统认为您的窗口不是最优先考虑的,则会将您的工作转移到后台。如果您强制窗口位于顶部,它将把所有资源放在窗口上,并删除正在处理的其他项目。它是真实的,它自DOS以来一直在这里。 http://en.wikipedia.org/wiki/Foreground-background您可以尝试在代码中的关键时刻调用以下内容。

Private Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long

答案 7 :(得分:0)

一种可能性是,您正在看到操作系统对某些GUI消息进行线程优先级提升/延迟的效果。

我假设你有一个“GUI&amp;其他东西“线程和多个工作线程。当没有GUI活动时,“Other Stuff”线程的优先级较低。当您摆动鼠标或超时时,“其他东西”线程会获得更高的优先级。

将工作线程更改为较低优先级,然后摆动鼠标,将确认或反驳此信息。