现在的位置: 首页 > 综合 > 正文

Chrome的线程体系

2013年01月30日 ⁄ 综合 ⁄ 共 7262字 ⁄ 字号 评论关闭

Chrome中的线程结构

网上已经有网友duguguiyu针对Chrome的线程体系做了很专业的描述了,应该说从原理上已经很完整了。本文主要在网友的基础上从代码实现的角度上进行一些补充和分析。
提到线程,我想大家关注的无非几点:线程的消息循环机制、多线程同步机制,线程间通信机制。
本文也是从大家关注的几点来重点描述。
在Chrome的代码中,与线程相关的文件主要在/base/base.vcproj中。
主要包含了以下相关文件:
task.h    线程执行的任何任务都是以task对象方式传递,有不同类型的task。
message_pump.h
message_pump_default.h
message_pump_default.cc
message_pump_win.h
message_pump_win.cc    消息泵类,消息调度,分发处理。
message_loop.h
message_loop.cc    消息循环机制,基本上每一个线程都有自己的消息循环,接收来自其他线程、UI甚至系统的消息。
thread.h
thread.cc     线程虚类类。Thread类在操作系统上层做了抽象,本身与平台无关
platform_thread.h
platform_thread_win.cc    Windows平台下线程的相关方法。在Windows平台下是CreateThread方法。
thread_local.h
thread_local.cc
thread_local_storage.h
thread_local_storage_win.cc    线程本地存储机制的实现(TLS)

其他还有一些辅助类,可以不用太关注,比如智能指针、一些简单的工具类等。我们重点分析上述的代码。

总体来说,Chrome的线程实现,主要运用了Command、Bridge和Observer三种模式。

线程类(Thread)的结构和简单流程

我们观看一下Thread.h中对Thread的定义

  1. //线程的抽象类
  2. // PlatformThread是底层的线程辅助类,实现不同平台下的真正的线程实现。
  3. // PlatformThread: Delegate,代理接口类,包含了ThreadMain函数。
  4. // Thread的子类实现,执行由PlatformThread传递到线程的回调函数中
  5. class Thread : PlatformThread::Delegate {
  6.  public:
  7.   struct Options {
  8.     //线程消息循环类型有三种,Default(普通的后台线程),UI,IO线程
  9.     //不同线程消息循环处理消息的方式有一些差异。
  10.     MessageLoop::Type message_loop_type;
  11.     //制定线程堆栈大小
  12.     size_t stack_size;
  13.     Options() : message_loop_type(MessageLoop::TYPE_DEFAULT), stack_size(0) {}
  14.     Options(MessageLoop::Type type, size_t size)
  15.         : message_loop_type(type), stack_size(size) {}
  16.   };
  17.   explicit Thread(const char *name);
  18.  
  19.   virtual ~Thread();
  20.  
  21.   bool Start();
  22.  
  23.   bool StartWithOptions(const Options& options);
  24.  
  25.   void Stop();
  26.   
  27.   void StopSoon();
  28.  
  29.   //获取消息循环实例,每一个线程的核心消息循环所在
  30.   MessageLoop* message_loop() const { return message_loop_; }
  31.  
  32.   const std::string &thread_name() { return name_; }
  33.  
  34.   PlatformThreadHandle thread_handle() { return thread_; }
  35.  
  36.   PlatformThreadId thread_id() const { return thread_id_; }
  37.  
  38.   bool IsRunning() const { return thread_id_ != 0; }
  39.  
  40.  protected:
  41.   virtual void Init() {}
  42.  
  43.   virtual void CleanUp() {}
  44.  
  45.   static void SetThreadWasQuitProperly(bool flag);
  46.  
  47.   static bool GetThreadWasQuitProperly();
  48.  
  49.  private:
  50. //实现了Platform_Thread类的Delegate接口
  51.   virtual void ThreadMain();
  52.  
  53.   bool thread_was_started() const { return startup_data_ != NULL; }
  54.  
  55.   struct StartupData;
  56.  
  57.   StartupData* startup_data_;
  58.  
  59.   PlatformThreadHandle thread_;
  60.  
  61.   MessageLoop* message_loop_;
  62.  
  63.   PlatformThreadId thread_id_;
  64.   // The name of the thread.  Used for debugging purposes.
  65.   std::string name_;
  66.  
  67.   friend class ThreadQuitTask;
  68.   DISALLOW_COPY_AND_ASSIGN(Thread);
  69. };

//平台相关的线程函数,不同平台有不同的实现机制。

class PlatformThread {

  1.  public:
  2.  
  3.   static PlatformThreadId CurrentId();
  4.  
  5.   static void YieldCurrentThread();
  6.  
  7.   static void Sleep(int duration_ms);
  8.  
  9.   static void SetName(const char* name);
  10.   class Delegate {
  11.    public:
  12.     virtual ~Delegate() {}
  13.     virtual void ThreadMain() = 0;
  14.   };
  15.   static bool Create(size_t stack_size, Delegate* delegate,
  16.                      PlatformThreadHandle* thread_handle);
  17.   static bool CreateNonJoinable(size_t stack_size, Delegate* delegate);
  18.   static void Join(PlatformThreadHandle thread_handle);
  19.  private:
  20.   DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread);
  21. };

Thread是一个与平台无关的线程抽象类。真正线程创建的相关函数在PlatformThread类中处理,而PlatformThread类的实现根据不同的操作系统又有不同的实现。在Windows平台下实现的是
platform_thread_win.cc。

我们跟踪一下线程函数的执行流程:

下面是platform_thread_win.cc中的实现:

 

 

  1. DWORD __stdcall ThreadFunc(void* closure) {
  2.   PlatformThread::Delegate* delegate =
  3.       static_cast<PlatformThread::Delegate*>(closure);
  4.   delegate->ThreadMain();
  5.   return NULL;
  6. }
  7. bool PlatformThread::Create(size_t stack_size, Delegate* delegate,
  8.                             PlatformThreadHandle* thread_handle) {
  9.   unsigned int flags = 0;
  10.   if (stack_size > 0 && win_util::GetWinVersion() >= win_util::WINVERSION_XP) {
  11.     flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
  12.   } else {
  13.     stack_size = 0;
  14.   }
  15.   *thread_handle = CreateThread(
  16.       NULL, stack_size, ThreadFunc, delegate, flags, NULL);
  17.   return *thread_handle != NULL;
  18. }

1.  *thread_handle = CreateThread(

      NULL, stack_size, ThreadFunc, delegate, flags, NULL);

ThreadFunc是线程函数,线程的执行体从此开始。

 

2. ThreadFunc函数实际调用的是delegate->ThreadMain();函数,由于Thread类实现了PlatformThread:Delegate代理类, 因此实际上调用的是Thread类的ThreadMain函数。

 

3.观察Thread类的ThreadMain函数实现,ThreadMain函数实际启动了消息循环MessageLoop,MessageLoop类接收不同的任务,并进行处理。

 

void Thread::ThreadMain() {

  // 创建消息循环.

  MessageLoop message_loop(startup_data_->options.message_loop_type);

  thread_id_ = PlatformThread::CurrentId();

  PlatformThread::SetName(name_.c_str());

  message_loop.set_thread_name(name_);

  message_loop_ = &message_loop;

  //进一步初始化

  Init();

  //设置启动完毕信号量信号

  startup_data_->event.Signal();

  //消息循环开始启动,线程阻塞于此

  message_loop.Run();

  //线程结束,收尾工作

  CleanUp();

  DCHECK(GetThreadWasQuitProperly());

  message_loop_ = NULL;

  thread_id_ = 0;

}

 

4.我们进一步跟踪一下MessageLoop的Run函数实现。可以发现实际上真正的线程调度工作是MessagePump类来实现的。

  1. void MessageLoop::Run() {
  2.   AutoRunState save_state(this);
  3.   RunHandler();
  4. }
  5. void MessageLoop::RunHandler() {
  6. #if defined(OS_WIN)
  7.   if (exception_restoration_) {
  8.     LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter();
  9.     __try {
  10.       RunInternal();
  11.     } __except(SEHFilter(current_filter)) {
  12.     }
  13.     return;
  14.   }
  15. #endif
  16.   RunInternal();
  17. }

 

  1. void MessageLoop::RunInternal() {
  2.   DCHECK(this == current());
  3.   StartHistogrammer();
  4. #if defined(OS_WIN)
  5.   //暂时我们可以过滤掉这个分支
  6.   if (state_->dispatcher) {
  7.     pump_win()->RunWithDispatcher(this, state_->dispatcher);
  8.     return;
  9.   }
  10. #endif
  11.   pump_->Run(this);
  12. }

 

5.由于MessagePump类是一个虚类,其根据不同的线程类型有不同的调度方法,我们先看看MessagePumpDefault类怎么实现的。

void MessagePumpDefault::Run(Delegate* delegate) {

  DCHECK(keep_running_) << "Quit must have been called outside of Run!";

 

  for (;;) {

    ScopedNSAutoreleasePool autorelease_pool;

 

    bool did_work = delegate->DoWork();

    if (!keep_running_)

      break;

 

    did_work |= delegate->DoDelayedWork(&delayed_work_time_);

    if (!keep_running_)

      break;

 

    if (did_work)

      continue;

 

    did_work = delegate->DoIdleWork();

    if (!keep_running_)

      break;

 

    if (did_work)

      continue;

 

    if (delayed_work_time_.is_null()) {

      event_.Wait();

    } else {

      TimeDelta delay = delayed_work_time_ - Time::Now();

      if (delay > TimeDelta()) {

        event_.TimedWait(delay);

      } else {

        // It looks like delayed_work_time_ indicates a time in the past, so we

        // need to call DoDelayedWork now.

        delayed_work_time_ = Time();

      }

    }

    // Since event_ is auto-reset, we don't need to do anything special here

    // other than service each delegate method.

  }

 

  keep_running_ = true;

}

这个函数实现了任务的调度算法,真正的工作又其实是MessageLoop类实现的(MessageLoop类实现了MessagePump:Delegate类)。
MessageLoop类包含了多个任务队列(即时执行任务,延时执行任务,Idle任务),并提供接口用户提交任务,

这个函数的实现有一些灵巧的调度算法包含在里面,我们将在第二节的消息循环中去详细讲解。

 

从上面的分析来看,Thread类并不真正的实现任何业务功能,仅仅实现了任务的调度,线程的启动、停止等功能,线程需要执行的工作都是由用户来提交的。这个任务就是Task类的提交的,这里用到了典型的Command模式。

 

线程实现中的一些模式

下面的图是设计模式中Command模式的典型结构图:

 

Command设计模式

 

引用《设计模式》中的话:

 

把一个请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化,对请求排队或者记录请求日志,以及支持可撤销的操作。

 

引用网友Venus的话

Command模式
Command 模式,是一种看上去很酷的模式,传统的面向对象编程,我们封装的往往都是数据,在Command模式下,我们希望封装的是行为。这件事在函数式编程中很正 常,封装一个函数作为参数,传来传去,稀疏平常的事儿;但在面向对象的编程中,我们需要通过继承、模板、函数指针等手法,才能将其实现。。。
应用Command模式,我们是期望这个行为能到一个不同于它出生的环境中去执行,简而言之,这是一种想生不想养的行为。我们做Undo/Redo的时 候,会把在任一一个环境中创建的Command,放到一个队列环境中去,供统一的调度;在Chrome中,也是如此,我们在一个线程环境中创建了 Task,却把它放到别的线程中去执行,这种寄居蟹似的生活方式,在很多场合都是有用武之地的。。。

对应在Chrome中线程的实现Task实现了Command模式中的Command接口,而类似CancelableTask,DeleteTask,ReleaseTask等类实现了COncreteCommand类。MessageLoop类相当于是一个Receiver,负责接收各种任务,并进行处理。

举一个例子:

 

 
Chrome中Command模式

 

这种设计方法,把线程的调度和实际的任务进行了分离,减少了模块之间的耦合性。同时具有:

1. 与我们传统的实现方式不同,抛弃了Callback函数方式,先注册,以后调用。采用Task方式,具有更多的自由度。

2. 一个Task对象和原先的请求发出者(Invoker)可以有不同的生命期。换言之,原先的请求发出者(Invoker)可能已经不在了,而Task对象本身仍然是活动的。

 

消息循环机制

未完待续

 

抱歉!评论已关闭.