我有一个带有几个线程循环的程序,您可以将任务发布到。其中一个线程循环是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();
}
}
}
答案 0 :(得分:3)
当MsgWaitForMultipleObjects[Ex]
表示由于一条或多条消息而返回时,您必须go into a loop processing all of them。您的代码只处理一条消息,这意味着第二条消息仍未处理。这就是为什么你永远不会收到WM_USER
消息的原因:在你有机会看到它之前你放弃了。
答案 1 :(得分:0)
不确定它是否是您的情况的罪魁祸首,但您应该组织代码,以便在目标线程已经有其消息循环后保证使用PostMessage()
。
新线程最初没有任何消息队列,只有在第一次尝试从中获取消息后才会创建它。我不确定MsgWaitForMultipleObjectsEx()
是否在这里计算,所以我建议通过调用PeekMessage()
来开始该线程,只是为了创建队列。
您的应用应保证在PeekMessage()
返回之前永远不会向帖子发帖/发送消息,否则消息就会丢失。
答案 2 :(得分: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);
感谢大家的帮助和建议!