簡體   English   中英

鼠標抖動/消息處理循環

[英]Mouse jiggling / message processing loop

我編寫了一個多線程程序,該程序進行了一些思考,並在此過程中打印出了一些診斷信息。 我注意到,如果在程序運行時搖動鼠標,則程序運行會更快。 現在我可以在這里詳細說明我的打印方式...但是我暫時不做介紹,因為我注意到在許多其他程序中,如果搖動鼠標,事情會更快發生,我想知道是否有這是許多人犯下的一些經典錯誤,其中消息循環由於不動的鼠標而以某種方式減慢了速度。

編輯:我的“打印”方法如下...我有一個豐富的編輯控件窗口來顯示文本。 當我要打印某些內容時,我會將新文本附加到窗口中現有的文本上,然后使用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);
}

我還沒有結束這個問題,因為我仍然想知道為什么原始代碼本身不能全部工作。

我在這里可以看到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種修改來檢查應用程序的行為嗎?

好吧,我不能完全幫助您,因為我們沒有足夠的信息,但是我遇到了類似的問題,除非我移動鼠標或經過一些(並非不重要的)延遲,否則我的應用程序將無法刷新。

在調查問題時,我發現基本上,如果沒有更多消息要處理,則GUI線程將休眠。 搖動鼠標將創建要發送到Windows的新Windows消息,從而使線程從睡眠狀態中喚醒。

我的問題是我在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的直接調用是否會覆蓋此設置。

好吧,這就是我能想到的。 至少您找到了一個修復程序,即使它不是最優雅的。

如果我沒記錯的話,WM_PAINT是一個優先級很低的消息,並且僅在消息隊列為空時才會被中繼。 同樣,Windows會將多個WM_PAINT消息合並為一個。 我可以看到鼠標移動導致更少的重繪事件,每個事件都處理了較大的更新,從而提高了性能。

您完全確定該程序確實運行得更快嗎? 還是更頻繁地刷新輸出?

您使用的是SendMessage還是PostMessage? 我很好奇,也許在這種特定環境下切換到其他方式會使事情“更好”地工作。

來自開發人員融合:

還有另一個類似於SendMessage的類似API,即PostMessage API。 兩者都需要相同的參數,但有一些細微的差別。 當使用SendMessage將消息發送到窗口時,將調用窗口過程,並且調用程序(或線程)等待消息被處理並回復,直到該調用程序才恢復其處理。 但是,這種方法有一個問題,那就是,如果正在忙於執行長指令的程序或已被掛起的程序,因此沒有時間響應該消息,又會因為您的程序正在等待而掛起程序。對於可能永遠不會到達的答復。 解決方案是使用PostMessage代替SendMessage。 另一方面,PostMessage可以立即返回到調用程序,而無需等待線程處理消息,因此可以避免程序掛起。 您必須使用哪一個取決於您的要求。

您的GUI窗口是否已最大化? 鼠標移動發生在應用程序窗口還是其他應用程序或桌面等其他窗口上,會發生這種情況嗎? 當鼠標移到您的應用上時,mouse_move消息將發送到您的消息隊列。 這可能會喚醒線程或強制執行WM_PAINT消息。

我懷疑打印實際上會更快。 我懷疑由鼠標移動引起的消息數量的增加迫使更多的窗口無效事件,因此文本更新發生在更細微的基礎上。 當不移動鼠標時,打印是否以較大的塊進行,例如一次20個字符對5個字符的塊?

您能否闡明更快打印的含義? 它是絕對的,例如每分鍾100個字符對每分鍾20個字符嗎? 還是這兩種方式更像是每分鍾100個字符,但是當鼠標靜止不動時它們會成塊顯示?

我認為這與前台和后台的處理有關。 如果操作系統認為您的窗口不是最重要的,它將把您的工作轉移到后台。 如果您將窗口置於頂部,它將把所有資源都放在窗口上,並放下其他正在處理的項目。 自DOS以來,它的真實存在。 http://en.wikipedia.org/wiki/Foreground-background您可以嘗試在關鍵時刻在代碼中調用以下內容。

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

一種可能性是您看到操作系統對某些GUI消息執行線程優先級提升/延遲的效果。

我假設您有一個“ GUI和其他內容”線程,以及多個工作線程。 如果沒有GUI活動,則“ Other Stuff”線程的優先級會降低。 擺動鼠標或超時時,“其他填充”線程將具有更高的優先級。

將工作線程更改為較低的優先級,然后擺動鼠標,將確認或駁斥此問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM