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, , , 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来进行挂起等待。
语言表达能力不行,不知到说清楚没有 。 不清楚在补吧
Chrome中的消息循环的更多相关文章
- TMsgThread, TCommThread -- 在delphi线程中实现消息循环
http://delphi.cjcsoft.net//viewthread.php?tid=635 在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使 ...
- TMsgThread, TCommThread -- 在delphi线程中实现消息循环(105篇博客,好多研究消息的文章)
在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使用很方便,但是有时候我们需要在线程类中使用消息循环,delphi没有提供. 花了两天的事件研究了 ...
- 安卓中的消息循环机制Handler及Looper详解
我们知道安卓中的UI线程不是线程安全的,我们不能在UI线程中进行耗时操作,通常我们的做法是开启一个子线程在子线程中处理耗时操作,但是安卓规定不允许在子线程中进行UI的更新操作,通常我们会通过Handl ...
- Looper.loop() android线程中的消息循环
Looper用于封装了android线程中的消息循环,默认情况下一个线程是不存在消息循环(message loop)的,需要调用Looper.prepare()来给线程创建一个消息循环,调用Loope ...
- Windows 消息循环(2) - WPF中的消息循环
接上文: Windows 消息循环(1) - 概览 win32/MFC/WinForm/WPF 都依靠消息循环驱动,让程序跑起来. 本文介绍 WPF 中是如何使用消息循环来驱动程序的. 4 消息循环在 ...
- TCommThread -- 在delphi线程中实现消息循环
http://www.techques.com/question/1-4073197/How-do-I-send-and-handle-message-between-TService-parent- ...
- Android应用程序线程消息循环模型分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6905587 我们知道,Android应用程序是 ...
- 深入探讨MFC消息循环和消息泵
首先,应该清楚MFC的消息循环(::GetMessage,::PeekMessage),消息泵(CWinThread::PumpMessage)和MFC的消息在窗口之间的路由是两件不同的事情.在MFC ...
- Android消息循环分析
我们的经常使用的系统中,程序的工作一般是有事件驱动和消息驱动两种方式,在Android系统中,Java应用程序是靠消息驱动来工作的. 消息驱动的原理就是: 1. 有一个消息队列.能够往这个队列中投递消 ...
随机推荐
- Eclipse导入android包错误
错误提示:Invalid project description… 解决方案:假设你的工作空间是workshop,那么你可以在你的workshop下新建一个文件夹,然后放入你的包,再在Eclipse中 ...
- highchars
var drawChart = function(sourceUrl) { $.ajax({ "type" : "post", "url" ...
- 括弧匹配检验(check)
/*题目:括弧匹配检验 检验给定表达式中括弧是否正确匹配 (两种括弧“( ) ”“[]" ,正确输出OK,错误则输出wrong. 2016年8月8日07:24:58 作者:冰樱梦 */ # ...
- opencv学习笔记(05)——操作相邻区域
下面的例子以灰度图像为例: #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp& ...
- TAG的用法和用途[转]
用一个例子来说明:一个combobox控件...一个textBox控件...一个datagridview控件!datagridview控件是连接数据库的...combobox和textBox是联合查询 ...
- Oracle查询出最最近一次的一条记录
需求:从一个表中查询数据,得到的数据为最新的一条记录. -------------建立测试表 --drop table TB ),dtDate date) -------------插入测试数据 ,' ...
- springboot pom 引用集合
<dependency><groupId>com.google.code.gson</groupId><artifactId>gson</arti ...
- hornetq 入门(1)
Hornetq 版本2.4.0final 需要JDK7及以上 Hornetq官网 Hornetq2.1中文手册 step1.启动服务端 1.1准备配置文件(配置说明参考官网手册) hornetq-c ...
- DataGridView 使用CheckBox选中行
在winform中使用checbox很多.上次那个项目里就用到了,写了一个不太好用,后来翻阅了一下微软提供的样码,我觉得有必要给大家分享一下. // This event handler manual ...
- spring中Bean的注入参数详解
字面值 一般指可用字符串表示的值,这些值可以通过<value>元素标签进行注入.在默认情况下,基本数据类型及其封装类.String等类型都可以采取字面值注入的方式,Spring容器在 ...