MessagePumpForUI的代码:
void MessagePumpForUI::DoRunLoop() {
// IF this was just a simple PeekMessage() loop (servicing all possible work
// queues), then Windows would try to achieve the following order according
// to MSDN documentation about PeekMessage with no filter):
// * Sent messages
// * Posted messages
// * Sent messages (again)
// * WM_PAINT messages
// * WM_TIMER messages
//
// Summary: none of the above classes is starved, and sent messages has twice
// the chance of being processed (i.e., reduced service time).
for (;;) {
// If we do any work, we may create more messages etc., and more work may
// possibly be waiting in another task group. When we (for example)
// ProcessNextWindowsMessage(), there is a good chance there are still more
// messages waiting. On the other hand, when any of these methods return
// having done no work, then it is pretty unlikely that calling them again
// quickly will find any work to do. Finally, if they all say they had no
// work, then it is a good time to consider sleeping (waiting) for more
// work.
bool more_work_is_plausible = ProcessNextWindowsMessage();
if (state_->should_quit)
break;
more_work_is_plausible |= state_->delegate->DoWork();
if (state_->should_quit)
break;
more_work_is_plausible |=
state_->delegate->DoDelayedWork(&delayed_work_time_);
// If we did not process any delayed work, then we can assume that our
// existing WM_TIMER if any will fire when delayed work should run. We
// don't want to disturb that timer if it is already in flight. However,
// if we did do all remaining delayed work, then lets kill the WM_TIMER.
if (more_work_is_plausible && delayed_work_time_.is_null())
KillTimer(message_hwnd_, reinterpret_cast<UINT_PTR>(this));
if (state_->should_quit)
break;
if (more_work_is_plausible)
continue;
more_work_is_plausible = state_->delegate->DoIdleWork();
if (state_->should_quit)
break;
if (more_work_is_plausible)
continue;
WaitForWork(); // Wait (sleep) until we have work to do again.
}
}
于I/O线程顺序不同,,UI线程首先执行windows系统消息,接着执行普通task,后面是延迟task,空闲task,最后调用WaitForWork()。
执行windows系统消息的是ProcessNextWindowsMessage函数,里面主要是一个典型的消息循环模型,首先调用PeekMessage取得消息,然后调用TranslateMessage和DispatchMessage把消息分发给正确的处理函数。UI线程本身创建的时候注册了一个默认处理函数WndProcThunk,它只处理kMsgHaveWork和WM_TIMER,其余的交给DefWindowProc。
与例如MFC的消息循环模型不同,MFC提取消息的API是GetMessage,这2者的主要区别是GetMessage会阻塞直到消息队列中出现了消息,PeekMessage则不会阻塞在这里,至于其他细节,可以查阅MSDN。为什么要使用PeekMessage,因为咱们的UI线程还有处理很多的task。
在看看UI线程的WaitForWork:
void MessagePumpForUI::WaitForWork() {
int delay = GetCurrentDelay();
if (delay < 0) // Negative value means no timers waiting.
delay = INFINITE;
DWORD result;
result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT,
MWMO_INPUTAVAILABLE);
if (WAIT_OBJECT_0 == result) {
MSG msg = {0};
DWORD queue_status = GetQueueStatus(QS_MOUSE);
if (HIWORD(queue_status) & QS_MOUSE &&
!PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE)) {
WaitMessage();
}
return;
}
DCHECK_NE(WAIT_FAILED, result) << GetLastError();
}
MsgWaitForMultipleObjectsEx一直等待新的输入消息,直到超时。
而MessagePumpDefault的消息循环则是这样的:
void MessagePumpDefault::Run(Delegate* delegate) {
DCHECK(keep_running_) << "Quit must have been called outside of Run!";
for (;;) {
mac::ScopedNSAutoreleasePool autorelease_pool;
bool did_work = delegate->DoWork();
if (!keep_running_)
break;
did_work |= delegate->DoDelayedWork(&delayed_work_time_);
if (!keep_running_)
break;
if (did_work)
continue;
did_work = delegate->DoIdleWork();
if (!keep_running_)
break;
if (did_work)
continue;
if (delayed_work_time_.is_null()) {
event_.Wait();
} else {
TimeDelta delay = delayed_work_time_ - Time::Now();
if (delay > TimeDelta()) {
event_.TimedWait(delay);
} else {
// It looks like delayed_work_time_ indicates a time in the past, so we
// need to call DoDelayedWork now.
delayed_work_time_ = Time();
}
}
// Since event_ is auto-reset, we don't need to do anything special here
// other than service each delegate method.
}
keep_running_ = true;
}
而默认线程只处理各种task,一个循环结束,最后实际上是调用WaitForSingleObject阻塞到超时。