闲话chromium线程模型

今天和朋友聊到关于chromium的线程模型的东西,在网上查了半天竟然没有哪篇能说清楚的,于是review了一下代码写点东西出来备忘一下,其实chromium(以下称为chrome)在对待多线程这个事情上的做法还是很值得一看的,chrome并不推荐过多的使用锁和threadsafe对象,在线程之间直接共享对象不可避免的设计操作原子性的问题;chrome的解决之道是通过command模式将对象隔离在各个线程之间,每个线程拥有自己的消息队列和消息循环(message_loop), 数据的访问都是通过消息传递实现的。

这部分的代码写得还是比较漂亮,但是网上的文章对于这部分的解读基本是一笔带过,其实深入到代码中,有不少值得学习的地方,比如chrome对于一个功能类抽象程度的把握,work_queue和incoming_queue的设计,智能指针的实现(scoped_refptr)等, 对提升架构能力,还有c++的功力很要帮助。

自顶向下的讲吧,从Thread类开始。在代码面前,没有magic,即使是google写的也一样。 Thread类顾名思义就是代表着一个线程对象。继承自PlatformThread::Delegate,这个delegate没什么好说的, 规定子类实现一个ThreadMain,这个函数在线程被创建后调用: src/base/threading/platform_thread_win.cc 代码:

DWORD __stdcall ThreadFunc(void* params) {
  ThreadParams* thread_params = static_cast<ThreadParams*>(params);
  PlatformThread::Delegate* delegate = thread_params->delegate;
  if (!thread_params->joinable)
    base::ThreadRestrictions::SetSingletonAllowed(false);
  delete thread_params;
  delegate->ThreadMain();
  return NULL;
}

// CreateThreadInternal() matches PlatformThread::Create(), except that
// |out_thread_handle| may be NULL, in which case a non-joinable thread is
// created.
bool CreateThreadInternal(size_t stack_size,
                          PlatformThread::Delegate* delegate,
                          PlatformThreadHandle* out_thread_handle) {
  PlatformThreadHandle thread_handle;
  unsigned int flags = 0;
  if (stack_size > 0 && base::win::GetVersion() >= base::win::VERSION_XP) {
    flags = STACK_SIZE_PARAM_IS_A_RESERVATION;
  } else {
    stack_size = 0;
  }

  ThreadParams* params = new ThreadParams;
  params->delegate = delegate;
  params->joinable = out_thread_handle != NULL;

  // Using CreateThread here vs _beginthreadex makes thread creation a bit
  // faster and doesn't require the loader lock to be available.  Our code will
  // have to work running on CreateThread() threads anyway, since we run code
  // on the Windows thread pool, etc.  For some background on the difference:
  //   http://www.microsoft.com/msj/1099/win32/win321099.aspx
  thread_handle = CreateThread(
      NULL, stack_size, ThreadFunc, params, flags, NULL);
  if (!thread_handle) {
    delete params;
    return false;
  }

  if (out_thread_handle)
    *out_thread_handle = thread_handle;
  else
    CloseHandle(thread_handle);
  return true;
}

这段代码是PlatformThread的windows实现,可以看见在ThreadFunc函数的传入参数中包含一个Thread类对象(实现了PlatformThread::Delegate)的最后显示的调用了Thread对象的ThreadMain方法。 我们回到src/base/threading/thread.cc中,看看ThreadMain的实现,发现线程对象在此时创建了一个MessageLoop对象,然后在Run函数中调用了这个MessageLoop对象的Run方法,这里提前剧透一下,MessageLoop.Run()是线程消息循环的主体,跑着一个for(;;),每次循环会检查当前线程的task队列是否有新task要做,如果没有的话(此时task队列为空),会wait一个event, 直到event被激发(有新的task被加入队列)。 和windows的消息队列很像是吧,没错,基本就是一个意思,甚至在windows平台的实现就是创建了个不可见窗口,复用windows的消息队列. Thread类基本就是这些,没什么神奇的是吧,至于一些WorkPool之类的实现,清楚了Thread和MessageLoop的关系后,基本也能看了 在有了对消息队列的大概认识后,看代码会轻松许多。

接下来我们要进入苦笔的MessageLoop MessageLoopProxy MessagePump三兄弟(?不应该是兄弟,有可能是父母和兄弟的关系吧 XD). 在写下去之前,我想稍微谴责一下命名这几个东西的家伙(具体原因后面会提到),因为当年也是一开始就读这块的代码,第一次看这三个东西几乎完全不知道他们是什么关系,到底做什么事情,他们几个接口都很可恶的差不多。接下来我们逐个往下看吧

首先,我们先看看MessageLoop类,文件位于src/base/message_loop.cc, src/base/message_loop.h 这个东西一看就很霸气外露,头文件和cc加起来居然有坑爹的1000多行,但是请不要被吓着,其实内容真心不多。先看类的定义class BASE_EXPORT MessageLoop : public base::MessagePump::Delegate { … }, 上来就和MessagePump扯上了点关系,这个Delegate是要求实现一系列的DoWork方法。既然这个delegate东西是messageloop和messagepump的纽带,我们就看看具体是个什么东西: 代码:

bool MessageLoop::DoWork() {
  if (!nestable_tasks_allowed_) {
    // Task can't be executed right now.
    return false;
  }

  for (;;) {
    ReloadWorkQueue();
    if (work_queue_.empty())
      break;

    // Execute oldest task.
    do {
      PendingTask pending_task = work_queue_.front();
      work_queue_.pop();
      if (!pending_task.delayed_run_time.is_null()) {
        AddToDelayedWorkQueue(pending_task);
        // If we changed the topmost task, then it is time to reschedule.
        if (delayed_work_queue_.top().task.Equals(pending_task.task))
          pump_->ScheduleDelayedWork(pending_task.delayed_run_time);
      } else {
        if (DeferOrRunPendingTask(pending_task))
          return true;
      }
    } while (!work_queue_.empty());
  }

  // Nothing happened.
  return false;
}

要不怎么说google是全世界最牛b的互联网公司,这代码写的清清楚楚掷地有声,不需要看懂每个函数的意义,一看过去就明白了大概的意思:首先尝试从workqueue中加载一下task, 如果为空的话直接推出循环,否则一个个task做,直到没有新的task为止(work_queue_.empty() == true)。

当时我看到这段心中大喊坑爹,不对啊,不是说好的message_loop嘛,这尼玛也不会永远的loop下去嘛,task做完怎么就break了,原来是我先入为主的以为此处实现的是整个消息队列的消息循环,但是我错了,这个message_loop实现的是挨个的做task, 和维护task的等待队列. 真正的loop在message_pump里! 然后我满怀期待的打开了message_pump.h,发现google为了跨平台,在每个平台用不同工具实现了各种各样的messagepump, 在message_pump.h中定义的是一个接口,但是我们找到了Run()方法的定义!不用说,这个是真正的消息循环,打开message_pump_default.cc一看,果不其然华丽丽的标准消息队列死循环出现在我的眼前。 代码:

void MessagePumpDefault::Run(Delegate* delegate) {
  DCHECK(keep_running_) << "Quit must have been called outside of Run!";

  for (;;) {
    mac::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_ - TimeTicks::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_ = TimeTicks();
      }
    }
    // Since event_ is auto-reset, we don't need to do anything special here
    // other than service each delegate method.
  }

  keep_running_ = true;
}

到现在,我们大概清楚了MessageLoop和MessagePump的关系: MessagePump维护着一个消息循环,然后每次循环从MessageLoop的task队列中运行各种task,直到task队列变空,然后wait,直到MessageLoop被插入新的task, 此时MessageLoop会激发MessagePump的waiting event,让消息循环继续运行。豁然开朗了 至于MessageLoopProxy,不过是一个对原始的MessageLoop的work_queue做了线程安全的封装而已。

当然,从Thread开始到这里我省略了很多的东西,比如MessageLoop的work_queue和incoming_queue互相倒腾的牛逼设计,如何实现线程安全(插入task队列时),各种平台的事件驱动的消息队列实现等等等等,随便哪个都能讲一卡车,这个乐趣还是留给自己去细细品读吧。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值