Chormium线程模型及应用指南

核心概念

设计上遵循以下原则:

1 不要在UI线程做任何阻塞式的I/O操作,以及其它耗时的操作,通过消息传递把各种操作传给相应用途的线程去做。

2 不鼓励线程加锁机制和线程安全对象。对象只存在一个线程,通过消息来实现线程之间的通信,线程之间不互相阻塞。通过callback对象实现跨线程请求。

一般情况下,我们应该利用一个现有的线程,尽量不要创建新的线程。UI线程实际上会被设置为不允许I/O操作,并且不允许等待。

线程列表

核心线程列表

所属进程 线程名称 类型 职责 实现
 Browser UI / BrowserThread Java Browser的主线程 content::BrowserThreadImpl
 Browser  Chrome_DBThread Native 负责数据库(SQLite)相关的操作,很多功能的实现会用到该线程。 content::BrowserThreadImpl
 Browser  Chrome_FileThread Native 文件的创建、删除、读写等。 content::BrowserThreadImpl
 Browser  Chrome_FileUserBlockingThread Native 用于读取与用户交互有关的数据,需要快速的响应。看到net log模块和appcache有用。 content::BrowserThreadImpl
 Browser  Chrome_ProcessLauncherThread Native 用于启动和终止子进程。 content::BrowserThreadImpl
 Browser  Chrome_CacheThread Native    
        content::BrowserThreadImpl
 Browser  IndexedDB Native IndexDB存储线程。 base::Thread
GPU  Chrome_InProcGpuThread Native GPU的单进程版本实现。 content::InProcessGpuThread
Child Processes Chrome_ChildIOThread Native 子进程的IO线程实现。 base::Thread
Renderer Chrome_InProcRendererThread Native Renderer进程的单进程版本实现。 content::InProcessRendererThread

其它线程

线程名称 Module 实现 说明
CookieMonsterClient CAW base::Thread  
CookieMonsterBackend CAW base::Thread  
CookieSyncManager WebView Runnable  
Chrome_libJingle_WorkerThread Browser base::Thread  
Blink Heap Marker Thread (*) Blink base::Thread  
Blink GC Sweeper (*) Blink base::Thread  
HTMLParserThread Blink base::Thread  
AsyncTransferThread gpu gpu::TransferThread  
BrowserBlocking Worker Browser base::SequencedWorkerPool 详见:Chromium中应用C/C++并发技术要点
SimpleCache Worker net base::SequencedWorkerPool  
Network File Thread net base::Thread  

线程结构

(以单进程模型说明)

Android下线程的消息结构

Chromium的线程结构

各个类的职责说明:

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

MessageLoop
主消息循环,原理上讲,它应该可以处理三种类型的消息,包括支持不同平台的消息。

事实上,如果让它处理所有这些消息,这会让其代码结构复杂不清难以理解。消息循环只需要三种类型:

  • 一种仅能处理自定义任务
  • 一种能处理自定义任务和 IO 操作
  • 一种是能处理自定义任务和 UI 消息。

很自然地,Chromium 定义一个基类 MessageLoop 用于处理自定义任务,两个子类对应于第二和第三种类型。

对于第二和第三种 MessageLoop 类型,它们除了要处理任务外,还要处理平台相关的消息,为了结构清晰,
chromium 定义一个新的基类及其子类来负责处理它们,这就是 MessagePump。MessagePump 的每个子类针对不同平台
和不同的消息类型。

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

摘自:<<理解WebKit和Chromium>>

Browser端线程结构

Browser端抛转线程消息,主要是基于BrowserThread提供的方法来完成的,如下:

// 检测所在的线程

DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

 

// 抛转任务到UI线程执行

BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,

        base::Bind(&AwLoginDelegate::HandleHttpAuthRequestOnUIThread,

                   this, (count->auth_attempts_ == 0)));

 

// 抛转任务到IO线程

BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,

      base::Bind(&AwLoginDelegate::ProceedOnIOThread,

                 this, user, password));

Render端的线程结构

Renderer端抛转消息,主要是基于MessageLoopProxy来完成。如下:

base::Closure closure =

        base::Bind(&CompositorOutputSurface::ShortcutSwapAck,

                   weak_ptrs_.GetWeakPtr(),

                   output_surface_id_,

                   base::Passed(&frame->gl_frame_data),

                   base::Passed(&frame->software_frame_data));

base::MessageLoopProxy::current()->PostTask(FROM_HERE, closure);

 

// input_event_filter.cc中的示例

io_loop_->PostTask(FROM_HERE,

                     base::Bind(&InputEventFilter::SendMessageOnIOThread,

                                this,

                                base::Passed(&message)));

 

// Blink platform implemetation

base::MessageLoopProxy::current()->PostTask(

      FROM_HERE,

      base::Bind(&PlatformEventObserverBase::SendFakeDataForTesting,

                 base::Unretained(observer), data));

gpu与Browser/Renderer的交互

线程安全

对于Java及Android的线程安全不再展开,可以参考附件的资料:<<Efficient Android Threading Asynchronous Processing Techniques for Android Applications>>

关于Java则推荐《Java并发实战》。

最常用的形式,将使用的对象定义为base::RefCountedThreadSafe。保证引用的对象不会被提前析构。

对于一些非线程安全的类可以使用NonThreadSafe提供Debug模式下线程安全确认。也可以应用ThreadCollisionWarner/ThreadChecker 确保运行线程与设计一致。

详见:Chromium中应用C/C++并发技术要点

参考: 如何安全的使用PostTask

任务的取消

除了任务按需要取消外,如果在宿主类析构后执行就可能导致崩溃。目前使用两种方式保证任务的取消:

WeakPtrFactory (WeakPtr)和CancelableTaskTracker, 它们析构时也会自动将任务取消。

CancelableTaskTracker可以参考Chromium官网的说明或是在FaviconCache中的应用。

class UserInputHandler : public base::RefCountedThreadSafe<UserInputHandler> {

  // Runs on UI thread.

  void OnUserInput(Input input) {

    CancelPreviousTask();

    DBResult* result = new DBResult();

    task_id_ = tracker_->PostTaskAndReply(

        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB).get(),

        FROM_HERE,

        base::Bind(&LookupHistoryOnDBThread, this, input, result),

        base::Bind(&ShowHistoryOnUIThread, this, base::Owned(result)));

  }

  void CancelPreviousTask() {

    tracker_->TryCancel(task_id_);

  }

  ...

 private:

  CancelableTaskTracker tracker_;  // Cancels all pending tasks while destruction.

  CancelableTaskTracker::TaskId task_id_;

  ...

};

对于WeakPtr,Chromium已经封装了一个WeakptrFactory供使用。可以参考GpuBrowserCompositorOutputSurface中的使用。使用方式比较简单,但没有CancelableTaskTracker通用。

下面是一个简单的示例(使用WeakPtrFactory<>最大的好处是不用修改类的定义.)

class MyObject {

 public:

  MyObject() : weak_factory_(this) {}

  void DoSomething() {

    const int kDelayMS = 100;

    MessageLoop::current()->PostDelayedTask(FROM_HERE,

        base::Bind(&MyObject::DoSomethingLater, weak_factory_.GetWeakPtr()),

        kDelayMS);

  }

  void DoSomethingLater() {

    ...

  }

 private:

  base::WeakPtrFactory<MyObject> weak_factory_;

};

*非线程安全,可以跨线程传递,但必须在一个线程上使用这个WeakPtr,即只能在运行在相同线程的任务上使用这个机制。
*类中WeakPtrFactory<Foo> weak_factory_的成员需要放在所有其他成员的后面,确保其他成员的析构函数执行的时候WeakPtrs还是无效的。

关于WeakPtr的进一步解释可以参考: Chromium中的weak_ptr,以及 关于SupportWeakPtr与WeakPtrFactory的选择.

时间: 01-06

Chormium线程模型及应用指南的相关文章

Netty系列之Netty线程模型

1. 背景 1.1. Java线程模型的演进 1.1.1. 单线程 时间回到十几年前,那时主流的CPU都还是单核(除了商用高性能的小机),CPU的核心频率是机器最重要的指标之一. 在Java领域当时比较流行的是单线程编程,对于CPU密集型的应用程序而言,频繁的通过多线程进行协作和抢占时间片反而会降低性能. 1.1.2. 多线程 随着硬件性能的提升,CPU的核数越来越越多,很多服务器标配已经达到32或64核.通过多线程并发编程,可以充分利用多核CPU的处理能力,提升系统的处理效率和并发性能. 相关

MyCat线程模型分析

参考MyCat权威指南,对MyCat-Server里面的线程模型做简要分析: 1. 线程模型图 根据MyCat权威指南,做出以下线程模型图: MyCat的线程模型主要分为三部分(: 网络通讯线程.业务线程和定时任务线程,下面分别介绍这些线程的使用: [温馨提示] 这里排除JVM自身使用的线程,只关注MyCat服务所使用的线程,如果需要详细了解MyCat里面使用的所有线程,请参考<MyCat权威指南>-> 开发篇 -> MyCat线程模型分析 2. 网络通讯线程 MyCat-Serv

Netty IO线程模型学习总结

Netty框架的 主要线程是IO线程,线程模型的好坏直接决定了系统的吞吐量.并发性和安全性. Netty的线程模型遵循了Reactor的基础线程模型.下面我们先一起看下该模型 Reactor线程模型 Reactor 单线程模型 单线程模型中所有的IO操作都在一个NIO线程上操作: 包含接受客户端的请求,读取客户端的消息和应答.由于使用的是异步非阻塞的IO,所有的IO操作不会阻塞,理论上一个线程就可以处理所有的IO操作. 单线程模型适用小容量的应用.因为在高并发应用 可导致以下问题 一个线程同时处

Dubbo线程模型(结合Linux线程数限制配置的实战经验分享)

Dubbo官方文档: 用户指南 >> 示例 >> 线程模型 配置标签: <dubbo:provider/> <dubbo:protocol/> 实战经验分享(属用性能调优): Linux用户线程数限制导致的java.lang.OutOfMemoryError: unable to create new nativethread异常 # vi/etc/security/limits.d/90-nproc.conf # Default limit fornumb

服务器端网络编程之线程模型

上一篇文章<服务器端网络编程之 IO 模型>中讲到服务器端高性能网络编程的核心在于架构,而架构的核心在于进程-线程模型的选择.本文将主要介绍传统的和目前流行的进程-线程模型,在讲进程-线程程模型之前需要先介绍一种设计模式: Reactor 模式,不明白的看这里<设计模式详解>,文中有一句话对 Reactor 模式总结的很好,引用下. Reactor 模式首先是事件驱动的,有一个或多个并发输入源,有一个Service Handler,有多个Request Handlers:这个Ser

多线程——线程模型

什么是程序? 安装在磁盘上的一段指令集合,它是静态的概念. 什么是进程? 它是运行中的程序,是动态的概念,每个进程都有独立的资源空间. 什么是线程? 线程,又称为轻量级进程,是程序执行流的最小单元,是程序中一个单一的顺序控制流程.线程是进程的一个实体,是被系统独立调度和分派的基本单位. 什么是多线程? 多线程则指的是在单个程序中可以同时运行多个不同的线程执行不同的任务. 多线程的特点 ①   一个进程可以包含一个或多个线程. ②   一个程序实现多个代码同时交替运行就需要产生多个线程. ③  

JS线程模型&amp;Web Worker

js线程模型 客户端javascript是单线程,浏览器无法同时运行两个事件处理程序 设计为单线程的理论是,客户端的javascript函数必须不能运行太长时间,否则会导致web浏览器无法对用户输入做出响应.这也是为什么Ajax的API都是异步的,以及为什么客户端Javascript不能使用一个简单的异步load()或者require()函数来加载javascript库 如果应用程序不得不执行太多的计算而导致明显的延迟,应该允许文档在执行这个计算之前完全载入,并且确保告诉用户正在进行计算并且浏览

线程模型的综述

本文首先介绍了一些线程基础,比如并发.并行.内存分配.系统调用.POSIX线程.接着通过strace分析了线程与进程的区别.最后以Android.Golang等线程模型进行了分析. 基础 1. 什么是并发(Concurrent),什么是并行(Parallels)? 并发指同时进行多个计算任务. 并行指通过切换时间片模拟进行多个计算任务. 详细可以参考Difference between concurrent programming and parallel programming - stack

Netty线程模型

一.Reactor模型 1.单线程模型 Reactor单线程模型,指的是所有的IO操作都在同一个NIO线程上面完成,NIO线程的职责如下: 1)作为NIO服务端,接收客户端的TCP连接: 2)作为NIO客户端,向服务端发起TCP连接: 3)读取通信对端的请求或者应答消息: 4)向通信对端发送消息请求或者应答消息 Reactor单线程模型示意图如下所示: 由于Reactor模式使用的是异步非阻塞IO,所有的IO操作都不会导致阻塞,理论上一个线程可以独立处理所有IO相关的操作.从架构层面看,一个NI