主要是自己做個學習筆記吧,我經驗也不是很豐富,以前學習多線程的時候就感覺寫多線程程序很麻煩。主要是線程之間要通信,要切線程,要同步,各種麻煩。我本身的工作經歷決定了也沒有太多的工作經驗,所以chrome的messageloop可以說是我用到的第一個成熟的線程消息封裝庫,用的很簡單,舒服。主要涉及MessageLoop和MessagePump這兩個類系。
以前不太清楚chrome當時在設計這兩個類時是如何分工的,今天又看了一下代碼,有了點感覺。MessagePump主要用來做消息循環, 與操作系統等平臺相關的部分都在MessagePump類裏, 針對不同的平臺有不同的實現,對messageloop封裝了平臺的不一致性。而MessageLoop主要是處理chrome自己的task機制的,這一部分。我們以windows平臺來進行代碼分析, MessagePump中的DoRunLoop是每個線程進行消息循環處理的地方。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
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的消息隊列中提取下一條消息進行處理。
1
|
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隊列。
1
|
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。
掛起等待用戶輸入消息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
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來進行掛起等待。
語言表達能力不行,不知到說清楚沒有 。 不清楚在補吧