看Chrome已经有一段时间了,但是一直都没有沉淀些内容下来,是该写写笔记什么的了,免得自己忘记了。看的都是Windows平台下的代码,所以记录也都是记录的。。。废话。。
那么首先,先从最基础的东西记录起吧:Chrome的线程模型和消息循环。
多线程的麻烦
多线程编程一直是一件麻烦的事情,线程执行的不确定性,资源的并发访问,一直困扰着众多程序员们。为了解决多线程编程的麻烦,大家想出了很多经典的方案:如:对资源直接加锁,角色模型,CSP,FP等等。他们的思想基本分为两类:一类是对存在并发访问资源直接加锁,第二类是避免资源被并发访问。前者存在许多问题,如死锁,优先级反转等等,而相对来说,后者会好很多,角色模型,CSP和FP就都属于后者,Chrome也是使用后者的思想来实现多线程的。
Chrome的线程模型
为了实现多线程,Chrome思路是简单且尽可能的少用锁,所以它在实现中并没有使用如角色模型之类的复杂的结构,而只是引入了自己的消息循环作为多线程的基础。它足够简单,方便使用,而且很容易实现跨平台。
相比平时的消息循环(如:Windows的消息循环,Linux中的epoll模型),它唯一增加的功能就是可以运行自定义的任务:Task。
如果在一个线程里面需要访问另一个线程的数据,则把接下来要运行的函数和参数包装成一个Task,并将其传递给另外一个线程,由另外一个线程来执行这个Task,从而避免关键数据的并发访问,而又因为任务执行是有顺序的,这样就保证了代码执行的确定性。
chrome-messageloop-task-simple:
其实,这就是一个典型的Command模式,而通过这个模式,简单的在线程之间传递Task,就实现了Chrome的多线程模型。
Task
1. Task
为了统一所有消息循环中的任务调用方式,所有的任务的基类都是这个Task类,他唯一的方法就是run(),MessageLoop只需要调用这个虚函数即可。
如果为了简化大家的开发,Chrome可谓下足了功夫,光是一个Task,就提供了各式各样的派生类供大家使用,并提供了良好的实现。
-
派生出来的Task有:CancalableTask,ReleaseTask,QuitTask等等。
-
根据调用条件的不同,将Task又分为即时处理的Task、延时处理的Task和Idle时处理的Task。
-
为了简化开发,还引入了RunnableMethod,封装对象的方法,减少我们自己实现Task的时间。
-
调用PostTask时,还需要传入一个TrackedObject,用于追踪Task的产生位置,为调试做准备。
2. RunnableMethod
RunnableMethod是一个很非常有用的类,这个方法通过模版将一个对象和他的方法和参数封装成一个Task,抛入另外一个线程去工作,其中为了保证对象的生命周期,对象的指针必须有引用计数,如果这个Task跨线程调用的话,这个引用计数必须是线程安全的。参数则通过Tuple来进行封装。在Task执行的时候通过另外一个模版将Tuple解开成参数即可。
线程和消息循环
Chrome将其线程分为了三类:普通线程,UI线程和IO线程。他们之间的区别是:
-
普通线程:只能执行Task,没有其他的功能。
-
UI线程:所有的窗口都需要跑在UI线程上,它除了能执行Task以外,还能执行和界面相关的消息循环。
-
IO线程:和本地文件读写,或者网络收发相关的操作都运行在这个线程上,它除了能执行Task以外,还能执行和IO操作相关的事件回调。
由于这三类线程中Task的执行部分基本是一样的,而其他的功能却完成不同,为了实现这不同的三类线程,Chrome将消息循环分成了两个部分:MessageLoop和MessagePump。
chrome-thread-and-messageloop:
MessagePump被提取出来负责执行Task的时机和处理线程本身的消息 ,如:UI线程的Windows消息,IO线程的IO事件。
MessageLoop则仅仅是做Task的管理,它实现了MessagePump的Delegate的接口,这样MessagePump就可以告诉MessageLoop何时应该处理Task了。
另外实现上虽然Chrome为这三种线程实现了三套MessageLoop,但是它们之间的区别,也仅限于暴露出现的MessagePump的接口不同而已。
chrome-messageloop-class-diagram:
消息循环之MessageLoop
1. 减少锁的请求
一般我们在实现任务队列时,会简单的将任务队列加锁,以避免多线程的访问,但是这样每取一次任务,都要访问一次锁。一旦任务变多,效率上必然成问题。
Chrome为了实现上尽可能少的使用锁,在接收任务时用了两个队列:输入队列和工作队列。
当向MessageLoop中内抛Task时,首先会将Task抛入MessageLoop的输入队列中,等到工作队列处理完成之后,再将当前的输入队列放入工作队列中,继续执行。
chrome-messageloop-task:
这样,就只有当将Task放入输入队列时才需要加锁,而平时执行Task时是无锁的,这样就减少了对锁的占用时间。
2. 延时任务
为了实现延时任务,在MessageLoop中除了输入队列和工作队列,还有两个队列:延迟延迟任务队列和需在顶层执行的延迟任务队列。
在工作队列执行的时候,如果发现当前任务是延迟任务,则将任务放入此延迟队列,之后再处理,而如果发现当前消息循环处于嵌套状态,而任务本身不允许嵌套,则放入需在顶层执行的延迟任务队列。
一旦MessagePump产生了执行延迟任务的回调,则将从这两个队列中拿出任务出来执行。
消息循环之MessagePump
MessagePump是用来从系统获取消息回调,触发MessageLoop执行Task的类,对于不同种类的MessageLoop都有一个相对应的MessagePump,这是为了将不同线程的任务执行触发方式封装起来,并且为MessageLoop提供跨平台的功能,chrome才将这个变化的部分封装成了MessagePump。所以在MessagePump的实现中,大家就可以找到很多不同类型的MessagePump:如MessagePumpWin,MessagePumpLibEvent,这些就是不同平台上或者不同线程上的封装。
Windows上的MessagePump有三种:MessagePumpDefault,MessagePumpForUI和MessagePumpForIO,他们分别对应着MessageLoop,MessageLoopForUI和MessageLoopForIO。
下面我们从底层循环的实现,如何实现延时Task等等方面来看一下这些不同的MessagePump的实现方式:
1. MessagePumpDefault
MessagePumpDefault是用于支持最基础的MessageLoop的消息泵,他中间其实是用一个for循环,在中间死循环,每次循环都回调MessageLoop要求其处理新来的Task。不过这样CPU还不满了?当然Chrome不会仅仅这么傻,在这个Pump中还申请了一个Event,在Task都执行完毕了之后,就会开始等待这个Event,直到下个Task到来时SetEvent,或者通过等待超时到某个延迟Task可以被执行。
2. MessagePumpForUI
MessagePumpForUI是用于支持MessageLoopForUI的消息泵,他和默认消息泵的区别是他中间会运行一个Windows的消息循环,用于分发窗口的消息,另外他还增加了一些和窗口相关的Observer等等。
各位应该也想到了一个问题:如果在某个任务中出现了模态对话框等等的Windows内部的消息循环,那么新来的消息应该如何处理呢?
其实在这个消息泵启动的时候,会创建一个消息窗口,一旦有新的任务到来,都会像这个窗口Post一个消息,这样利用这个窗口,即便在Windows内部消息循环存在的情况下,也可以正常触发执行Task的逻辑。
既然有了消息窗口,那么触发延时Task的就很简单了,只需要给这个窗口设置一个定时器就可以了。
3. MessagePumpForIO
MessagePumpForIO是用于支持MessageLoopForIO的消息泵,他和默认消息泵的区别在于,他底层实际上是一个用完成端口组成的消息循环,这样不管是本地文件的读写,或者是网络收发,都可以看作是一次IO事件。而一旦有任务或者有延时Task到来,这个消息泵就会向完成端口中发送一个自定义的IO事件,从而触发MessageLoop处理Task。
相关推荐
chromium message loop 封装使用的一个跨线程通信demo,内含有chromium base库(部分抽取),以及消息循环跨线程通信的一个使用案例。
Docker 中的 Chromium 浏览器 如果您想知道如何在 Docker 中运行 Chromium。 这个 docker image 可能是一个解决方案。 在 Docker 中运行 Chrome 的需要有助于部署测试。 例如,您可能有另一个 docker 镜像正在运行,...
Chromium是一个开放源代码的浏览器项目,旨在为所有Internet用户构建一种更安全,更快,更稳定的方式来体验Web。该站点包含设计文档,体系结构概述,测试信息等,以帮助您学习构建和使用Chromium源代码。
Chromium是一个开放源代码的浏览器项目,旨在为所有Internet用户构建一种更安全,更快,更稳定的方式来体验Web。该站点包含设计文档,体系结构概述,测试信息等,以帮助您学习构建和使用Chromium源代码。
用于Chromium的Docker容器 这是Chromium的Docker容器。 可通过现代Web浏览器(无需在客户端上进行安装或配置)或任何VNC客户端来访问应用程序的GUI。 非常感谢@jlesage。 该图像基于 表中的内容 取消RAID 用户/...
Chrome的SOAAP工具该存储库包含用于将SOAAP应用于Chromium的工具。 要使用它们,您需要: 构建及其和自定义版本。 查看您感兴趣的Chromium版本: $ git submodule init$ git submodule update --reference . v42 # ...
与其他具有Web浏览器愿景的其他Chromium分支不同,ungoogled-chromium本质上是Chromium的直接替代品。 ungoogled-chromium进行了一些调整,可以增强隐私,控制和透明度。 但是,几乎所有这些功能都必须手动激活或...
Portapps未与Ungoogled Chromium:trade_mark:或其任何子公司或分支机构有任何关联,关联,授权,认可或以任何方式与之正式关联。 可以在上找到Ungoogled Chromium:trade_mark:官方网站。 Ungoogled Chromium:trade...
如何从此水龙头安装Chromium 通过公式: brew install domt4/chromium/chromium 通过木桶: brew cask install domt4/chromium/mac-chromium 然后将以常规方式获取更新: brew update brew upgrade Chrome发布...
高朗ChromeDocker映像包Golang和Chromium,基于golang:1-alpine 。执照麻省理工学院
节点Chrome 您的NodeJS项目的Chromium二进制文件node-chromium使您可以轻松地将二进制文件添加到项目中,并将其用于自动化,测试,Web抓取或只是为了娱乐。为什么选择Chrome? 是由The Chromium Project开发和维护的...
当前版本安装brew tap mtslzr/marmaduke-chromiumbrew updatebrew install marmaduke-chromium酒桶此水龙头包括Marmaduke Chromium的三个版本: marmaduke-chromium包括Google Sync和wildvine(适用于Netflix的DRM...
ChromeChromium开发版本:macOS和Windows-所有编解码器仅64位
AndroidChromium 简单的介绍 谷歌浏览器android版源程序 该项目是世界一流的android体系结构 澄清项目业务逻辑可以完全适合国内公司工程师 该项目将长期跟踪和更新Google浏览器内核版本,欢迎加入星号 注意 如果您...
AndroidChromium 简单的介绍 谷歌浏览器android版源程序 该项目是世界一流的android体系结构 澄清项目业务逻辑可以完全适合国内公司工程师 该项目将长期跟踪和更新Google浏览器内核版本,欢迎加入星号 注意 如果您...
该存储库包含有关对信息。 它还为贡献者和审阅者提供了一些工具,以协助完成。 三星对Chromium开源项目的清单。 Chromium开源项目的代码审查中的列表。
按照重要性降序(即最重要的目标优先),ungoogled-chromium是Google Chromium,在不依赖于Google Web服务的情况下,ungoogled-chromium尽可能地保留了默认的Chromium体验。 与其他具有Web浏览器愿景的其他Chromium...
按照重要性的降序(即最重要的目标在前),ungoogled-chromium 是 Google Chromium,不依赖于 Google 网络服务,ungoogled-chromium 尽可能地保留了默认的 Chromium 体验。 与其他拥有自己的网络浏览器愿景的 ...
过滤掉Chrome/电子标准错误的虚假日志消息。 例子 $ # just filter stderr, works in bash/zsh $ electron index.js 2> >( silence-chromium ) $ # pipe stderr to stdout and filter both, works in sh/bash/zsh $ ...
java实现新建文件夹安卓Chrome 为 Android 构建 Chromium Content Shell 、 Test ...开发人员来说是个好消息,但您可能仍然有充分的理由希望走这样的道路: 您想要更多地控制浏览器; 也许您想添加