2

タスクを投稿できるいくつかのスレッドループを持つプログラムがあります。これらのスレッド ループの 1 つが UI スレッド ループです。投稿されたタスクだけでなく、ウィンドウ メッセージも処理する必要があるため、WM_USER メッセージを送信して、ディスパッチ ループでスレッドを起動します。

問題は、時々 (特に や のような他のウィンドウ メッセージがたくさんあるWM_PAINT場合WM_RESIZE)、私のWM_USERメッセージがスレッドを起こさないことです。理由はわかりませんが、関数は呼び出しPostMessageからスレッドを起こさないようです。MsgWaitForMultipleObjectsEx

これは次のようになります (簡単にするためにいくつかの言い換えがあります)。

#define HaveWorkMessage (WM_USER + 100)

class ThreadLoopUI {
public:
    ThreadLoopUI()
        : myHaveWork(0) {}

    void PostTask(Task& aTask) {
        {
            ScopedLock lock(myMutex);
            myTaskQueue.push_back(aTask);
        }

        ScheduleWork();
    }

    void ScheduleWork() {
        if (InterlockedExchange(&myHaveWork, 1)) {
            // No need to spam the message queue
            return;
        }

        if (!PostMessage(myHWnd, HaveWorkMessage, reinterpret_cast<WPARAM>(this), 0)) {
            std::cerr << "Oh noes! Could not post!" << std::endl;
        }
    }

    void Run() {
        for (;;) {
             // SIMPLIFICATION, SEE EDIT BELOW
             DWORD waitResult = MsgWaitForMultipleObjectsEx(0, NULL, (DWORD)INFINITE, QS_ALLINPUT, MWMO_INPUTAVAILABLE);

             if (waitResult == WAIT_FAILED) {
                  std::cerr << "Well, that was unexpected..." << std::endl;
                  continue;
             }

             bool doWork = false;

             MSG message;
             if (PeekMessage(&message, NULL, 0, 0, PM_REMOVE)) {

                   if (message == HaveWorkMessage) {
                        doWork = true;
                        InterlockedExchange(&myHaveWork, 0);
                   }

                   // Send the message on to the window procedure
                   TranslateMessage(&message);
                   DispatchMessage(&message);
             }

             if (doWork) {
                 // Process all tasks in work queue
             }
        }
    }
private:
    HWND                 myHwnd;
    Mutex               myMutex;
    std::vector<Task>   myTaskQueue;
    LONG volatile       myHaveWork;
}

編集:上記への直接呼び出しMsgWaitForMultipleObjectsExは単純化されました。私は実際に次のような関数を呼び出します。

void WaitForMessages() {
    DWORD waitResult = MsgWaitForMultipleObjectsEx(0, NULL, (DWORD)INFINITE, QS_ALLINPUT, MWMO_INPUTAVAILABLE);

    if (waitResult == WAIT_OBJECT_O) {
        // Comment from the Chromium source:
        // A WM_* message is available.
        // If a parent child relationship exists between windows across threads
        // then their thread inputs are implicitly attached.
        // This causes the MsgWaitForMultipleObjectsEx API to return indicating
        // that messages are ready for processing (Specifically, mouse messages
        // intended for the child window may appear if the child window has
        // capture).
        // The subsequent PeekMessages call may fail to return any messages thus
        // causing us to enter a tight loop at times.
        // The WaitMessage call below is a workaround to give the child window
        // some time to process its input messages.
        MSG message = {0};
        DWORD queueStatus = GetQueueStatus(QS_MOUSE);
        if (HIWORD(queueStatus) & QS_MOUSE &&
            !PeekMessage(&message, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE)) 
        {
            WaitMessage();
        }               
    }
}
4

3 に答える 3

3

MsgWaitForMultipleObjects[Ex]1 つまたは複数のメッセージが原因で返された場合は、それらすべてを処理するループに入る必要があります。コードは 1 つのメッセージのみを処理します。つまり、2 番目のメッセージは未処理のままです。それがあなたがメッセージを決して受け取らない理由ですWM_USER:あなたはそれを見る機会を得る前にあきらめました.

于 2013-02-21T13:24:18.027 に答える
0

私は今、原因を突き止めました。場合によっては、メッセージ ループの外で Windows によってキューからメッセージがディスパッチされるようです (つまり、WindowProcedure自動的に送信されます)。これを解決するために、次のWindowProcedureように変更しました。

LRESULT CALLBACK 
ThreadLoopUI::WindowProcedure( 
    HWND    aWindowHandle, 
    UINT    aMessage, 
    WPARAM  aWParam, 
    LPARAM  aLParam )
{
    switch (aMessage)
    {
    case HaveWorkMessage:
        // This might happen if windows decides to start dispatch messages from our queue
        ThreadLoopUI* threadLoop = reinterpret_cast<ThreadLoopUI*>(aWParam);

        InterlockedExchange(&threadLoop->myHaveWork, 0);

        // Read the next WM_ message from the queue and dispatch it
        threadLoop->PrivProcessNextWindowMessage();

        if (threadLoop->DoWork())
        {
            threadLoop->ScheduleWork();
        }

        break;
    }

    return DefWindowProc(aWindowHandle, aMessage, aWParam, aLParam);

皆さんの助けと提案に感謝します!

于 2013-03-01T09:20:03.223 に答える
0

それがあなたのケースの原因であるかどうかはわかりませんがPostMessage()、ターゲットスレッドがすでにメッセージループを持っている後に使用されることが保証されるように、コードを整理する必要があります。

新しいスレッドは、最初はメッセージ キューを持たず、メッセージを取得しようとする最初の呼び出しの後にのみ作成されます。MsgWaitForMultipleObjectsEx()ここで がカウントされるかどうかわからないのでPeekMessage()、キューを作成するためだけに、 への呼び出しでスレッドを開始することをお勧めします。

アプリは、PeekMessage()返される前にスレッドにメッセージを投稿/送信しないことを保証する必要があります。そうしないと、メッセージが単に失われる可能性があります。

于 2013-02-21T09:41:07.647 に答える