主要是自己做个学习笔记吧,我经验也不是很丰富,以前学习多线程的时候就感觉写多线程程序很麻烦。主要是线程之间要通信,要切线程,要同步,各种麻烦。我本身的工作经历决定了也没有太多的工作经验,所以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中的消息循环的更多相关文章

  1. TMsgThread, TCommThread -- 在delphi线程中实现消息循环

    http://delphi.cjcsoft.net//viewthread.php?tid=635 在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使 ...

  2. TMsgThread, TCommThread -- 在delphi线程中实现消息循环(105篇博客,好多研究消息的文章)

    在delphi线程中实现消息循环 在delphi线程中实现消息循环 Delphi的TThread类使用很方便,但是有时候我们需要在线程类中使用消息循环,delphi没有提供.   花了两天的事件研究了 ...

  3. 安卓中的消息循环机制Handler及Looper详解

    我们知道安卓中的UI线程不是线程安全的,我们不能在UI线程中进行耗时操作,通常我们的做法是开启一个子线程在子线程中处理耗时操作,但是安卓规定不允许在子线程中进行UI的更新操作,通常我们会通过Handl ...

  4. Looper.loop() android线程中的消息循环

    Looper用于封装了android线程中的消息循环,默认情况下一个线程是不存在消息循环(message loop)的,需要调用Looper.prepare()来给线程创建一个消息循环,调用Loope ...

  5. Windows 消息循环(2) - WPF中的消息循环

    接上文: Windows 消息循环(1) - 概览 win32/MFC/WinForm/WPF 都依靠消息循环驱动,让程序跑起来. 本文介绍 WPF 中是如何使用消息循环来驱动程序的. 4 消息循环在 ...

  6. TCommThread -- 在delphi线程中实现消息循环

    http://www.techques.com/question/1-4073197/How-do-I-send-and-handle-message-between-TService-parent- ...

  7. Android应用程序线程消息循环模型分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6905587 我们知道,Android应用程序是 ...

  8. 深入探讨MFC消息循环和消息泵

    首先,应该清楚MFC的消息循环(::GetMessage,::PeekMessage),消息泵(CWinThread::PumpMessage)和MFC的消息在窗口之间的路由是两件不同的事情.在MFC ...

  9. Android消息循环分析

    我们的经常使用的系统中,程序的工作一般是有事件驱动和消息驱动两种方式,在Android系统中,Java应用程序是靠消息驱动来工作的. 消息驱动的原理就是: 1. 有一个消息队列.能够往这个队列中投递消 ...

随机推荐

  1. phpcms v9 源码解析(4)content模块下的index.php文件的init()方法解析

    在了解index.php中的init函数的时候,让我们先看看最开始的几行代码 1-5  第二行, defined('IN_PHPCMS') or exit('Nopermission resource ...

  2. 《PHP和MySQL Web开发》精彩的地方收录

    1.用SESSION来做的购物车,做成数组,用isbn对应书的数量作为二维数组保存 $new GET传值加入购物车,submit是修改数量,提交后的表单,通过历遍原来的数组,对应isbn修改最新的数量 ...

  3. 让ImageView可以使用gif的方法

    在自己的包中添加MyGifView.java(直接复制,粘贴),读取gif资源在MyGifView中第20行读取: MyGifView.java: package com.zzw.testgifuse ...

  4. 无法访问Fedora的samba共享

    配置好samba服务后,却发现windows无法访问.经过多次试验与fedora的防火墙有关系. 关闭防火墙: #service iptables stop 或清空规则: #iptables -F w ...

  5. JDBC 连接数据库

          JAVA使用JDBC访问数据库的步骤: 1.     得到数据库驱动程序   (导包) 2.     创建数据库连接  3.     执行SQL语句 4.     得到结果集 5.     ...

  6. ERROR 1005 (HY000): Can't create table'matrix.system_log' (errno: 150)

    CREATE TABLE `user` (`id` bigint(32) NOT NULL AUTO_INCREMENT ,`name` varchar(32) CHARACTER SET utf8 ...

  7. RDD的转换操作---RDD转换过程

    1) union(otherRDD)RDD-->UnionRDD2) groupByKey(numPartitions)RDD-->ShuffledRDD-->MapPartitio ...

  8. mysql卸载

    先执行mysql安装程序,点击移除,然后再删除对应的安装路径,必要的时候还要删除注册表信息.

  9. C# File

    http://msdn.microsoft.com/zh-cn/library/system.io.file(v=vs.110).aspx using System; using System.IO; ...

  10. Problem 1016 咒文卷轴 优先队列+前缀和+rmq

    题目链接: 题目 Problem 1016 咒文卷轴 Time Limit: 3000 mSec Memory Limit : 131072 KB 问题描述 小Y 是一个魔法师,有一天他获得了一卷神秘 ...