理解WebKit和Chromium: 消息循环(Message Loop)

转载请注明出处:http://blog.csdn.net/milado_nju/

# 消息循环

## 概述

前面介绍了线程间如何传递chromium自定义任务(task),那么在线程内,消息循环(messageloop)是如何处理这所有的消息和任务呢?本章节重点介绍消息循环的工作原理。

在Chromium里,需要处理三种类型的消息:chromium自定义的任务,Socket或者文件等IO操作以及用户界面(UI)的消息。这里面,chromium自定义任务是平台无关的,而后面两种类型的消息是平台相关的。回忆一下前面多线程模型章节中列举的众多线程,例如主线程(UI线程)需要处理UI相关的消息和自定义任务;IO线程则需要处理Socket和自定义任务;History线程则只需要处理自定义任务;其它线程需要处理消息类型不会超出以上三个线程。根据三个组合,chromium定义和实现三种相对应的类来支持它们,下面让我们来看看具体的实现吧。

##Chromium中主要的类

这是本章中最重要的三个基类,依次介绍它们:

类RunLoop:一个辅助类,主要封装消息循环MessageLoop类,其本身没有特别的功能,主要提供一组公共接口被调用,其实质是调用MessageLoop类的接口和实现。

类MessageLoop:主消息循环,原理上讲,它应该可以处理三种类型的消息,包括支持不同平台的消息。事实上,如果让它处理所有这些消息,这会让其代码结构复杂不清难以理解。因此,根据上面对各个线程的分析,消息循环也只需要三种类型,一种仅能处理自定义任务,一种能处理自定义任务和IO操作,一种是能处理自定义任务和UI消息。很自然地,Chromium定义一个基类MessageLoop用于处理自定义任务,两个子类对应于第二和第三种类型。如下图所示。对于第二和第三种MessageLoop类型,它们除了要处理任务外,还要处理平台相关的消息,为了结构清晰,chromium定义一个新的基类及其子类来负责处理它们,这就是MessagePump。MessagePump的每个子类针对不同平台和不同的消息类型。事实上,不仅如此,消息处理的主循环也在MessagePump中,这有些令人意外,后面会详细介绍。因此,MessageLoop通过实现MessagePumpDelegate的接口来负责处理Chromium自定义任务。如下图所示。

类MessagePump: 一个抽象出来的基类,可以用来处理第二和第三种消息类型。对于每个平台,它们有不同的MessagePump的子类来对应,这些子类被包含在MessageLoopForUI和MessageLoopForIO类中。


结合上面的图,针对三种类型的消息循环,三种典型的线程在不同平台所使用的类如下:

 

主线程

IO线程

History线程

Linux

MessageLoopForUI, MessagePumpGtk

MessageLoopForIO, MessagePumpLibEvent

MessageLoop, MessagePumpDefault

Windows

MessageLoopForUI, MessagePumpForUI

MessageLoopForIO, MessagePumpForIO

MessageLoop, MessagePumpDefault

Mac

MessageLoopForUI, MessagePumpMac

MessageLoopForIO, MessagePumpLibEvent

MessageLoop, MessagePumpDefault

Android

MessagePumpForUI

Android.os.Looper

SystemMessageHandler

MessagePumpAndroid

MessageLoopForIO, MessagePumpLibEvent

MessageLoop, MessagePumpDefault

对于所有平台来说,History线程所使用的类是一样的;UI线程和IO线程分别对应不同的MessagePump。相信大家注意到了,最后一个平台Android跟其它的稍有不同,那就是它的UI线程,原因在于主循环在Java层,用户界面的事件派发机制都在Java代码来处理,因而需要在Android的消息循环机制中加入对自定义任务的处理,后面会作介绍。

## 无限循环

这里面还有一个重要的部分需要介绍,那就是消息循环的主逻辑。该循环本质就是一个无限循环,不停的处理消息循环接收到的任务和消息,直到需要推出为止。如前面所述,主消息循环的逻辑在MessagePump中,而MessageLoop只是负责处理自定义的任务,MessagePump通过调用MessagePumpDelegate来达到该目的。下面是IO线程的消息处理主逻辑的时序图。


前面章节我们介绍过MessageLoop有任务队列来保存需要处理的任务,这些任务可能有不同的优先级,例如需要即时处理,或者延迟处理,或者Idle时处理。上图中,当调用DoWork时候,首先将出入任务队列(incoming task)拷贝到工作任务队列中,然后依次执行该队列中的任务。之后,同理处理调用DoDelayWork和DoIdleWork。当这些处理完后,它会阻塞和等待在IO操作。

对于Android的UI线程来说,情况稍有不同,因为主循环逻辑是由Java层的Android.os.Looper来控制的,所以MessagePumpAndroid其实是被Looper调用的。当它被调用时,其会把执行任务的工作交给MessagePumpDelegate,这里也就是MessageLoopForUI来完成,如下图所示的UI线程的消息循环主逻辑。Java层的SystemMessageHandler和C++层的MessagePumpAndroid其实都是辅助Looper调用执行chromium的自定义类的。


最后还有一个问题,就是如何等待自定义的任务。假设现在MessageLoop没有任务和消息需要处理,它应该等待Socket或者IO或者任务,OS系统支持Socket和IO唤醒它,问题是如何等待自定义任务呢?总不能忙式的检查吧,那样太耗费资源了。Chromium设计了一个巧妙的方法来解决该问题,以MessagePumpLibEvent为例:在Linux平台上,该类创建一个管道,它等待读取这个管道的内容,当有自定义的新任务到来时,写入一个字节到这个管道,从而MessageLoop被唤醒,非常地简单和直接。

## 源文件目录

base/message_ loop.h|cc

base/message_pump.h|cc

  消息循环相关的众多类基本上都位于该目录中,文件名以”message_”开头

base/android/java/src/org/chromium/base/SystemMessageHandler.java

 Android平台下支持消息循环的辅助类

## 参考文献

1.      http://bigasp.com/archives/478

By yongsheng@chromium.org

posted on 2013-01-24 21:23  王大王  阅读(633)  评论(0编辑  收藏  举报

导航