Chomium MessageLoop实现原理

Chromium Message Loop

什么是Message Loop ?
Message loop,即消息循环,在不同系统或者机制下叫法也不尽相同,它是一种等待和分派消息的编程结构,是经典的消息驱动机制的基础。

消息循环可以使所有的任务都变成异步驱动,在线程间通信有很好的作用,也可以避免一些条件竞争场景,例如把所有的ui操作都通过MessageLoop抛到主线程完成。

本文分析Chromium中消息循环的设计和实现思路。

主要类和功能

MessagePump(LibEvent)
如果是UI线程和IO线程会使用MessagePumpLibEvent 实现,其他线程则使用MessageDefault. 前者利用libEvent 的事件驱动特性,后者使用event 信号来控制线程阻塞和唤醒。

当线程启动时,线程的入口函数ThreadMain会创建并启动 MessagePump, 开始任务轮转,该类会不断的向ThreadControllerWithMessagePumpImpl 发出DoWork指令,驱动其向任务队列获取任务。当队列中仅剩延时任务或者有新入队的任务时,MessagePump 会在正确的时间将自己唤醒。

在MessagePumpLibEvent::Init 时会创建一对匿名管道,分别为 input 端和 output 端。当储存的任务全部完成时,MeesagePump 会利用 Libevent 监听 output 端是否可读,此时该线程会处于阻塞的状态。当其他有其他线程向该线程抛任务时,且检测到该线程正处于阻塞状态时,会向input端写入一个字节,由于libEvent事件驱动的特性,线程被唤醒重新执行调度。定时任务也是同样方案,当检测到当前只剩延时任务时,MessagePump会计算下一个任务还需要多长时间,创建TimeEvent,并且利用libevent将自己阻塞,等待定时完成。
MessagePumpDefault是通过 event信号量来实现线程阻塞和启动的,两者有什么区别呢?Chromium原文注释是

  // This type of pump only supports tasks and timers.
  DEFAULT,

  // This type of pump also supports native UI events (e.g., Windows
  // messages).
  UI,
  
  // This type of pump also supports asynchronous IO.
  IO,

这样看来,使用LibEvent的方式可能是为了潜在的跨进程唤醒。


ThreadControllerWithMessagePumpImpl

主要负责任务队列 SequenceManagerImpl 和 messagePump 之间的交互,负责向 SequenceManagerImpl 获取一批任务,然后在通过 TaskAnnotator 执行该任务,TaskAnnotator的作用是计算和处理一些堆栈信息和debug信息,以便打印堆栈和调试时可以查看任务执行过程。

在一批任务执行完成之后,还需要向 SequenceManagerImpl 请求下一个任务的状态 并返回给MessagePump (立即执行/延时/不存在下一个任务), MessagePump会根据这个返回值决定继续轮转还是阻塞。
另外这还是一个中转类,被包括RunLoop在内的大多数类持有,任务调度的大部分流程都有参与。


RunLoop 使用base::Thread 创建一个线程成功之后,该线程的入口函数MainThread 会创建 RunLoop对象,持有 ThreadControllerWithMessagePumpImpl, 负责启动线程和MessageLoop,线程完成后的回收等。

SequenceManagerImpl 负责任务的储存和调度,是核心类之一。 其成员 active_queues_ 中保存了至少一个 TaskQueue, 默认存在一个 default_tq (线程初始化时由SeqeuenceManagerThreadDelegate 调度创建)。每个task_queue都会设定一个优先级,优先级影响调度策略,目前仅在BrowserTaskQueue看到多个优先级的任务队列。
当 向SequenceManagerImpl 请求下一个任务时,会创建 TaskQueueSelector 对象,该对象负责从 active_queue_ 中选取一个WorkQueue,并从WorkQueue中获取一个任务。

TaskQueueSelector 由SequenceMangerImpl 持有,负责任务队列 WorkQueue的选取。
其包含两个work_queue_sets(immediate 和 delay), 里面存放所有TaskQueue持有的workQuque,并且按照优先级建立映射,以便获取任务时计算优先级。


TaskQueue(Impl) 管理任务的入队和出队,通过CreateTaskRunner 创建并暴露task_runner对象,其他线程可以通过静态方法获取该线程的TaskRunner并向该线程抛任务。

在这里插入图片描述

每个 TaskQueueImpl 中都持有了两个对象 : MainThreadOnly 和 AnyThread,这两个对象分别表示当前线程可访问和任意线程可访问。
其中 MainThreadOnly 持有 delayed_work_queue 和 immediate_work_queue 两个WorkQueue类型的任务队列,注意这里的 delayed_work_queue 存放的实际上都是到期的延时任务,因此可以直接调度。MainThreadOnly 中还存在一个优先队列 delayed_incoming_queue, 延时任务都会push到这里,优先队列可以保证每次取出的都是最延时最小的任务。

AnyThread 中仅存在TaskDeque类型的 immediate_incoming_queue。
每当SequenceMnagerImpl 通过 SelectNextTaskImpl 获取任务时,会通过标志位来检查所有的TaskQuque.main_thread_only. immediate_woek_queue_是否为空,然后会 使用 TakeTaskFromWorkQueue 将any_thread中的即时任务全部移动过来。

另外 SelectNextTaskImpl 还会调用 MoveReadyDelayedTasksToWorkQueues 检查 delayed_incoming_queue 中的任务是否到期,并移动到 delayed_work_queue 中,以便在下一个lo

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值