chrome源码解析系列:Chrome消息系统(1)

chrome中有很多闪光点地方,它的消息系统就一快纯金,要看chrome 源码,必须要过消息系统这关。
   本来这本部打算写在上一章的,考虑内容涵盖范围太广的,打算另开一章来写 chrome的消息系统,回头在去上一章做个比较有概括力的总结。

本章的思路是按照一下逻辑来展开的:
  1:消息系统的概述(消息系统静态模型和动态模型的一个简单的介绍)
  2:一个消息系统的生死因果(细说MessageLoopUI类)
  3:消息系统的分类(对chrome的几种消息做消息介绍)

1:消息系统的概述

(1)消息系统的静态结构

这是消息系统的第一个部分,先看看和消息相关的静态类图:

(图片画的太大了,这张看的不是很清楚,需要大图片,可以去我的新浪空间去看 大图片)

上面那张图片涉及到设计软件时几种采用的伎俩,下面会一一分析:

伎俩一:handle-body,pimpl, 桥接

MessageLoop基本就是MessagePump的一个代理类,

扩充了一些MessagePump没有的功能,

chrome本意可以能是想让Message来处理所有事务,

而这个messgaePump可以做到对调用者透明。

但是对于研究chrome代码的CODER来说,

了解MessagePump是不可推卸的责任。

后面都会详细分析的。

MessagePump和MessageLoop采用用了一种handle-body或称pimpl的伎俩,

按照牛逼点的叫法叫着桥接

关于此伎俩可以参考四人帮的设计模式

伎俩二:委托

Chrome中已经用烂了改伎俩,基本上所有代码中都能看见此手法。

一个类的内部操作委托给外面的一个对象,通过抽象接口。

如上面代码所示,MessagePump把  DoWork  DoDelayWork, DoIdelWork通过MessagePump::Deletgate 的接口委托给M essageLoop去处理,这样比较好的解决了代码间的耦合问题。

类似伎俩在消息系统这块还有很多:

MessagePump::Watcher           《对应handle的事件处理》

MessagePump::Observer          《监视Windows消息的处理》

MessagePump::Dispatcher         《Windows本地消息的分派》

MessageLoop::DestructionObserver 《用来跟踪MessageLoop的稀构》

这些都是委托的接口,只不过名字不像MessagePump::Deletgate这么张扬而已。

把委托的接口类写到类的内部,

对于chrome这种写法我是我以前没有考虑过的,

是个非常清晰的代码风格,

coder一看就能知道这个委托接口是干吗的。

伎俩三:线程本地存储

一个线程怎么获取到当前线程的消息循环对象,通过线程本地存储。

所谓的线程本地存储就是把一个变量和一个线程相关联.

最简单的说是我定义了一个全局的 int n变量,我把n设置为线程本地存储变量,当开启了4个线程,这四个线程都修改了 n这个变量,结果是n对应于每个线程都有一份拷贝,即四个线程四份n的值。

Chrome中是把 MessageLoop对象设置为线程本地存储,

每个线程获取线程的消息循环很简单,直接通过调用MessageLoop一个静态的current()方法返回当前线程的消息循环。

比如在一个线程里面运行一个消息循环

  1. MessageLoop::current()->Run();

伎俩四:关于chrome 添加任务的方式

注意观察MessageLoop中的任务队列,会发现它有四个队列,如下:

incoming_queue_              《所有任务都是首先添加到该队列中》

work_queue_                   《当前的工作队列》

delayed_work_queue_            《推迟指定时间执行的任务队列》

deferred_non_nestable_work_queue_《不能重入被推迟执行任务队列》

现在要弄明白每个队列的在MesageLoop中的语意,只有先了解了语意才能知道为什么要这样设计。

incoming_queue_ 和 work_queue_  队列的区别?

由于添加队列设计到多线程操作,必然需要对其加锁。

程序的设计原则是能不用锁打死也不用,现在chrome的MessageLoop需要段的加入消息,循环内部需要不断地处理消息,如果每次添加和取消息都要对队列加锁,那么是比会影响其执行效率。

如是,chrome采用了这样的一个措施,采用两个队列:

一个用来从外部线程收集任务的队列(incoming_queue),一个是供内部使用的队列(work_queue),并且保证从work_queue队列中取出任务不需要加锁。

遵循以下三个原则:

1:每次添加一个任务都需要加锁(任务被添加到incoming_queue)

2:每次从work_queue队列中取出任务不需要加锁。

3:在work_queue为空时需要把incoming_queue队列中的内容取出来,取出的操作需要加锁

这样循环的效率可以提高 40-50%。

  1. void MessageLoop::ReloadWorkQueue() {
  2.   // We can improve performance of our loading tasks from incoming_queue_ to
  3.   // work_queue_ by waiting until the last minute (work_queue_ is empty) to
  4.   // load.  That reduces the number of locks-per-task significantly when our
  5.   // queues get large.
  6.   if (!work_queue_.empty())
  7.     return// Wait till we *really* need to lock and load.
  8.   // Acquire all we can from the inter-thread queue with one lock acquisition.
  9.   {
  10.     AutoLock lock(incoming_queue_lock_);
  11.     if (incoming_queue_.empty())
  12.       return;
  13.     std::swap(incoming_queue_, work_queue_);
  14.     DCHECK(incoming_queue_.empty());
  15.   }
  16. }

关于 delayed_work_queue_  和deferred_non_nestable_work_queue 队列后面再来介绍。

(2)消息动态流程

和WINDOWS的传统消息机制类似,但是它扩充了WINDOWS的消息循环机制,使得它能够支持事件对象和任务。


这张图片简单的描述了消息的线程的创建和通讯过程(看大图片)。


       Chrome对自己的线程采取一种lazyCreate 的策略,就是每个线程在需要的时候才被创建。线程之间是通过g_browser_process这个全局对对象来获取相应的线程中的消息循环,然后再通过消息循环中的PostTask 系列的函数来发送任务到相应的线程队列中去。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值