[Chrome源码阅读] 理解Thread类实现

2021年6月30日 3点热度 0条评论 来源: zero_lee

Chrome中的Thread类实现比较有意思,没有复杂的接口,没有很多可调控的参数,譬如线程优先级什么的。

线程类Thread定义在base命名空间里,是一个比较通用的基础类。位于文件src\base\thread.h。Thread继承于PlatformThread::Delegate类,除了提供易用的接口之外,比较中要的内容就是实现了线程入口函数,这种继承框架是基于跨平台考虑。跟平台相关的代码放在了PlatformThread类里,线程启动时,转调用它Delegate类的函数体就可以。以下是PlatformThread::Delegate类的说明:

// A simple thread abstraction that establishes a MessageLoop on a new thread.
// The consumer uses the MessageLoop of the thread to cause code to execute on
// the thread.  When this object is destroyed the thread is terminated.  All
// pending tasks queued on the thread's message loop will run to completion
// before the thread is terminated.
class Thread : PlatformThread:: Delegate {

PlatformThread::Delegate类定义在\src\base\platform_thread.h文件中,是一个接口。

  // Implement this interface to run code on a background thread.  Your
  // ThreadMain method will be called on the newly created thread.
  class Delegate {
   public:
    virtual ~ Delegate() {}
    virtual void ThreadMain () = 0;
  };

base::Thread实现了ThreadMain函数。

PlatformThread类里函数全部都是static,譬如Create线程函数:

  static bool Create (size_t stack_size, Delegate * delegate,
                     PlatformThreadHandle* thread_handle );

传入delegate参数,提供线程入口函数,比如Thread,这个函数将会创建线程,在Windows平台会调用WIN32API函数CreateThread:

  *thread_handle = CreateThread(
      NULL, stack_size , ThreadFunc, delegate, flags , NULL);

其中ThreadFunc函数是一个全局函数入口,简单的转调用delegate提供的ThreadMain函数体:

DWORD __stdcall ThreadFunc( void* closure ) {
  PlatformThread:: Delegate* delegate =
      static_cast<PlatformThread ::Delegate*>( closure);
  delegate-> ThreadMain();
  return NULL;
}

以上方法我们都干过。

在Thread类的实现,每个Thread都会有一个message_loop,用来处理各种message,更准确的说应该是Task。每个线程正常结束前,应该执行一个叫做ThreadQuitTask的task,它的执行体相当简单,就是调用MessageLoop的Quit函数。Thread类里维护着一个MessageLoop的指针, 你可能会认为它是一个指向堆上对象的指针,但是不是的,它是一个指向栈上对象的指针,类似的数据成员还有StartData* startup_data_(用来判断线程是否启动的标志量)。我们来看下Thread的线程函数体是怎样的:

void Thread :: ThreadMain() {
  // The message loop for this thread.
  MessageLoop message_loop (startup_data_ -> options. message_loop_type );

  // Complete the initialization of our Thread object.
  thread_id_ = PlatformThread ::CurrentId ();
  PlatformThread:: SetName (name_ . c_str());
  message_loop. set_thread_name (name_ );
  message_loop_ = & message_loop ;

  // Let the thread do extra initialization.
  // Let's do this before signaling we are started.
  Init();

  startup_data_-> event .Signal ();
  // startup_data_ can't be touched anymore since the starting thread is now
  // unlocked.

  message_loop. Run ();

  // Let the thread do extra cleanup.
  CleanUp();

  // Assert that MessageLoop::Quit was called by ThreadQuitTask.
  DCHECK( GetThreadWasQuitProperly ());

  // We can't receive messages anymore.
  message_loop_ = NULL ;
  thread_id_ = 0;
}

注意的是,这个函数是执行在一个新的线程里。首先,它实例化一个局部MessageLoop,其实它就是这个线程的MessageLoop,用Thread::message_loop()函数返回的就是它的指针。接着调用message_loop::Run()函数来执行task,直到messageloop遇到ThreadQuitTask类型的task。注意的是,在线程执行过程,这个messageloop始终是可以访问的。线程销毁时,这个局部的messageloop就是销毁,线程的message loop也就销毁了。这种方式大大减少了对象生命期管理的问题,而且也减少了堆上对象创建销毁的开销。挺好。

类似的技巧在
StartupData
 
startup_data_
 
;也得到了体现,这个指针也是一个临时对象的指针,对象创建在栈上。其实在线程函数开始运行之后,这个指针将会是一个野指针,因为临时对象已经销毁。

bool Thread::Start() {
  return StartWithOptions(Options());
}

bool Thread::StartWithOptions(const Options& options) {
  DCHECK(!message_loop_);

  SetThreadWasQuitProperly(false);

  StartupData startup_data(options);
  startup_data_ = &startup_data;

  if (!PlatformThread::Create(options.stack_size, this, &thread_)) {
    DLOG(ERROR) << "failed to create thread";
    startup_data_ = NULL;  // Record that we failed to start.
    return false;
  }

  // Wait for the thread to start and initialize message_loop_
  startup_data.event.Wait();

  DCHECK(message_loop_);
  return true;
}

传给StartWithOptions函数的options只是一个临时对象,而且start_data也是一个局部的,Thread::startup_data_指向的是这个局部变量。当线程函数启动之后,Start函数返回后,这个startup_data_存储的指针也就变成了野指针。Thread::Stop函数会将其reset到NULL。

    原文作者:zero_lee
    原文地址: https://blog.csdn.net/zero_lee/article/details/7903911
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。