2009-10-21 99 views
2

我写了一个多线程的程序,它可以做一些思考并打印出一些诊断信息。我注意到,如果我在程序运行时轻敲鼠标,程序运行得更快。现在我可以在这里详细介绍我是如何打印的......但是现在我会暂时停下来,因为我注意到在其他许多程序中,如果鼠标抖动,事情会发生得更快,不知是否有是许多人做出的一个经典错误,其中消息循环以某种方式被不移动的鼠标放慢。鼠标抖动/消息处理循环

编辑:我的“打印”方法如下...我有一个丰富的编辑控制窗口来显示文本。当我想打印某些内容时,我将新文本附加到窗口中的现有文本上,然后使用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); 
} 

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

+0

你有任何基准来证明这一点吗?我听说,流行的JiggleMark 2009非常好。 – 2009-10-21 09:20:12

+0

不要关闭它。对于下一个有类似问题的人可能会有用。 – 2009-11-03 17:01:31

回答

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

你能检查你的应用程序的行为与INFINITE超时和上述3修改包括?

+1

重要的是,SendMessage将等待,直到在拥有目标窗口的线程上调用消息处理函数(在这种情况下为PeekMessage)。由于MsgWait *并不是单独由SendMessage根据这些标志唤醒的,它将等待直到生成一些未发送的消息。第二件重要的事情是GetWindowText和SetWindowText在内部执行SendMessage。 – Komat 2009-10-25 17:16:09

+0

干得好!答案是你的数字3。 – Mick 2009-10-27 10:47:33

0

你完全确定程序运行速度真的快吗?还是输出更频繁地刷新?

+0

我确信程序的非GUI方面正在全速运行 - 但看起来,打印过程似乎被鼠标不移动所阻塞。我要编辑一些更详细的内容来回答我原来的问题......这将需要一两分钟的时间。 – Mick 2009-10-21 09:45:16

1

嗯,我不能完全帮助你,因为我们没有足够的信息,但我有类似的问题,我的应用程序不会刷新,除非我移动鼠标或一些(无意义的)延迟后。

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

我的问题是我在OnIdle(MFC,不知道你)函数中做了什么处理,并且在处理一次之后,线程会进入睡眠状态。

我不认为这是你的问题,因为你似乎从你的线程发布了一条windows消息(WM_PAINT),我没有在我的情况下做(应该唤醒gui线程),但也许这可以帮助你朝正确的方向解决你的问题?

编辑:我虽然关于它一点,也许有一个WM_PAINT的特殊情况(就像你忘记调用Invalidate或什么的,我不是Windows编程的专家),所以也许尝试发布另一条消息,如WM_USER到你的应用程序,看看它是否能解决你的问题(这应该确保唤醒我认为的gui线程)。同时发布SendMessage函数的完整调用可能会有所帮助。编辑2:好吧,看到你对凯利法语上面的评论之后,你似乎有和我完全一样的症状,所以我会猜测,无论出于何种原因,你对PostMessage的调用似乎不会唤醒GUI线程或相似的东西。你为PostMessage的第一个参数传递了什么?我在我的例子中做的是用我的应用程序调用PostMessage参数WM_USER,0,0。您也可以尝试PostThreadMessage变体,同时将主线程的当前线程ID保留在变量中(请参阅GetCurrentThreadId)。

你也可以尝试在你的对象上调用Invalidate。如果一个对象需要重新绘制,Windows会保留内存,如果不需要,它将不会执行它。我不知道是否直接调用WM_PAINT覆盖或不。

这就是我所能想到的。至少你找到了一个修补程序,即使它不是最优雅的。

+0

查看更多详细信息添加到我原来的帖子。 – Mick 2009-10-23 12:49:27

+0

那么函数MsgWaitForMultipleObjects似乎在接收到一个输入(QS_ALLEVENTS)时会被唤醒,所以它让我认为你的WM_PAINT消息没有被接收到(无论什么原因)或者不会导致线程唤醒。我会检查SendMessage函数。它似乎包含很多特殊性。也许尝试像PostMessage之类的变体?我不确定,但看起来我们接近这个问题。 – n1ckp 2009-10-23 13:03:06

+0

只是这第二次尝试PostMessage() - 但留下完全相同的症状。 – Mick 2009-10-23 13:16:09

1

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

0

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

从developerfusion摘自:

还有另一个类似的API,它究竟 作品像SendMessage函数和 是PostMessage的API。两者都需要 相同的参数,但有一个轻微的 差异。当一个消息被发送到 与SendMessage消息的窗口,该窗口 过程被调用和主叫 程序(或线程)等待要处理的 消息和答复 回来,直到那时主叫 程序不会恢复其 处理。有一件事是错 这种做法不过,那就是如果 程序正忙执行长 指令或具有 一直挂着,因此根本来不及反应 的消息将依次挂你 程序过于程序因为你的程序将会在 的等待回复中可能永远不会到达 。解决方案是使用PostMessage而不是SendMessage来使用 。 另一方面PostMessage将 立即返回给调用程序 而不用等待线程处理消息 ,从而节省您的 程序挂起。你必须使用哪一个 取决于你的 要求。

+0

我使用的是SendMessage - 我尝试了PostMessage,但它对问题没有任何影响。 – Mick 2009-10-23 13:19:33

0

您的GUI窗口最大化了吗?是否发生鼠标移动发生在您的应用程序窗口或其他窗口(如另一个应用程序或桌面)上?当鼠标移过您的应用程序时,mouse_move消息会发送到您的消息队列。这可能会唤醒线程或强制WM_PAINT消息。

我怀疑打印速度实际上会更快。我怀疑由鼠标移动引起的消息数量的增加迫使更多的窗口失效事件,因此文本更新发生在更精细的基础上。当鼠标没有被移动时,打印是否发生在更大的块中,例如每次20个字符与5个字符的块?

您能否通过更快的打印来说明您的意思?它是绝对的吗?像每分钟100个字符与每分钟20个字符?或者它更像是每分钟100个字符,但当鼠标仍然显示时,它们会以块形式出现?

+0

如果鼠标静止,则根本不会打印。如果我等了很长时间,只要稍微触摸一下鼠标,然后突然出现所有打印。它似乎不关系鼠标的位置。没有一个窗口是最大化的。 – Mick 2009-10-23 13:47:08

0

我认为它与前景和背景处理有关。如果操作系统认为你的窗口不是最重要的,它会将你的工作转移到后台。如果您迫使窗户置于顶部,则会将其所有资源投入到窗户上,并放弃正在处理的其他项目。它的真实和自DOS以来一直在这里。 http://en.wikipedia.org/wiki/Foreground-background您可能会尝试在代码中的关键时刻调用以下代码。

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

一种可能性是您正在看到OS对某些GUI消息执行线程优先级提升/延迟的影响。

我假设你有一个“GUI &其他东西”线程和多个工作线程。当没有GUI活动时,“其他材料”线程进入较低优先级。当您滑动鼠标或超时时,“其他材料”线程的优先级更高。

将工作线程更改为较低的优先级,然后摆动鼠标,将确认或反驳这一点。