Chrome中的消息循环
主要是自己做个学习笔记吧,我经验也不是很丰富,以前学习多线程的时候就感觉写多线程程序很麻烦。主要是线程之间要通信,要切线程,要同步,各种麻烦。我本身的工作经历决定了也没有太多的工作经验,所以chrome的messageloop可以说是我用到的第一个成熟的线程消息封装库,用的很简单,舒服。主要涉及MessageLoop和MessagePump这两个类系。
以前不太清楚chrome当时在设计这两个类时是如何分工的,今天又看了一下代码,有了点感觉。MessagePump主要用来做消息循环, 与操作系统等平台相关的部分都在MessagePump类里, 针对不同的平台有不同的实现,对messageloop封装了平台的不一致性。而MessageLoop主要是处理chrome自己的task机制的,这一部分。我们以windows平台来进行代码分析, MessagePump中的DoRunLoop是每个线程进行消息循环处理的地方。
void MessagePumpForUI::DoRunLoop() { 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. } }
这块儿代码通过一个for的死循环来维持线程的运行, 同时进行系统消息的处理和task的处理,从代码看可以分为循环可以分为如下几个部分:伪码描述:
for(;;) { 处理windows系统消息 执行task队列中的一个task 执行delayedTask队列中的一个task。 if(还有其他任务(more_work_is_pausiable)), continue; else 挂起线程,等待消息进行唤醒 }
下面分别从几部分进行分析:
处理windows消息
循环先从Windows的消息队列中提取下一条消息进行处理。
bool more_work_is_plausible = ProcessNextWindowsMessage();
bool MessagePumpForUI::ProcessNextWindowsMessage() { // If there are sent messages in the queue then PeekMessage internally // dispatches the message and returns false. We return true in this // case to ensure that the message loop peeks again instead of calling // MsgWaitForMultipleObjectsEx again. bool sent_messages_in_queue = false; DWORD queue_status = GetQueueStatus(QS_SENDMESSAGE); if (HIWORD(queue_status) & QS_SENDMESSAGE) sent_messages_in_queue = true; MSG msg; if (message_filter_->DoPeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) return ProcessMessageHelper(msg); return sent_messages_in_queue; }
在processNextWindowsMessage函数中主要处理 window的窗口消息,它的返回值 表示输入队列中是否还有其他消息待处理,这样可以避免多调用一次MsgWaitForMultipleObjectsEx。
具体的ProcessMessageHelper代码如下,单独的WM_QUIT来进行单独推出处理。
bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { TRACE_EVENT1("base", "MessagePumpForUI::ProcessMessageHelper", "message", msg.message); if (WM_QUIT == msg.message) { state_->should_quit = true; PostQuitMessage(static_cast<int>(msg.wParam)); return false; } // While running our main message pump, we discard kMsgHaveWork messages. if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_) return ProcessPumpReplacementMessage(); if (CallMsgFilter(const_cast<MSG*>(&msg), kMessageFilterCode)) return true; WillProcessMessage(msg); if (!message_filter_->ProcessMessage(msg)) { if (state_->dispatcher) { if (!state_->dispatcher->Dispatch(msg)) state_->should_quit = true; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } DidProcessMessage(msg); return true; }
执行task
处理了一个windows消息后, 然后通过MessagePump::Delegate的接口,调用MessageLoop的DoWork操作,来处理task队列。
more_work_is_plausible |= state_->delegate->DoWork();
bool MessageLoop::DoWork() { if (!nestable_tasks_allowed_) { // Task can't be executed right now. return false; } for (;;) { ReloadWorkQueue(); if (work_queue_.empty()) break; // Execute oldest task. do { PendingTask pending_task = work_queue_.front(); work_queue_.pop(); if (!pending_task.delayed_run_time.is_null()) { AddToDelayedWorkQueue(pending_task); // If we changed the topmost task, then it is time to reschedule. if (delayed_work_queue_.top().task.Equals(pending_task.task)) pump_->ScheduleDelayedWork(pending_task.delayed_run_time); } else { if (DeferOrRunPendingTask(pending_task)) return true; } } while (!work_queue_.empty()); } // Nothing happened. return false; }
这个函数中通过循环来找到一个处理当前taskQueue的一个task进行执行, 将delayed的task 存入DelayedWorkQueue.
执行DelayedTask
bool MessageLoop::DoDelayedWork(TimeTicks* next_delayed_work_time) { if (!nestable_tasks_allowed_ || delayed_work_queue_.empty()) { recent_time_ = *next_delayed_work_time = TimeTicks(); return false; } // When we "fall behind," there will be a lot of tasks in the delayed work // queue that are ready to run. To increase efficiency when we fall behind, // we will only call Time::Now() intermittently, and then process all tasks // that are ready to run before calling it again. As a result, the more we // fall behind (and have a lot of ready-to-run delayed tasks), the more // efficient we'll be at handling the tasks. TimeTicks next_run_time = delayed_work_queue_.top().delayed_run_time; if (next_run_time > recent_time_) { recent_time_ = TimeTicks::Now(); // Get a better view of Now(); if (next_run_time > recent_time_) { *next_delayed_work_time = next_run_time; return false; } } PendingTask pending_task = delayed_work_queue_.top(); delayed_work_queue_.pop(); if (!delayed_work_queue_.empty()) *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time; return DeferOrRunPendingTask(pending_task); }
比对时间,如果到了delayedtask的执行时机,执行delayed task。
挂起等待用户输入消息
void MessagePumpForUI::WaitForWork() { // Wait until a message is available, up to the time needed by the timer // manager to fire the next set of timers. 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) { // 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 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来进行挂起等待。
语言表达能力不行,不知到说清楚没有 。 不清楚在补吧