chromium多线程间通信机制分析

  chromium中会创建多个线程,分别执行不同的操作,它会为每个线程创建一个消息队列,当一个线程需另外一个线程执行某个任务时,会向该线程发送一个closure,使得该closure在目标线程执行。从而达到多线程通信的目的。其中closure的本质为一个特殊的callback函数。
  chromium中callback的创建调用过程如下代码所示:

void MyFunc(int i, const std::string& str) {}  
base::Callback<void(const std::string&)> cb = base::Bind(&MyFunc, 23);  
cb.Run("hello world");

  上述代码中base::Bind创建了一个callback对象,该对象绑定了一个函数及其的一个参数,该对象可以看做时一个closure对象,chromium需要进行线程间通信时,即将它发送到目标线程中,目标线程调用Run方法,可以使得函数MyFunc在目标线程被调用。
  同时从上述代码可以看出,函数MyFunc的参数可以在绑定时指定,也可以在目标线程中调用时候指定,而函数MyFunc可以是定义的全局函数,也可以时类的成员函数,但是在调用base::bind创建该callback对象时,需要绑定一个对应的类对象。实例代码如下所示

class MyClass {
public:
    void MyFunc(int i, const std::string& str)
};
MyClass myclass = new Myclass;
base::Callback<void(void)> cb = base::bind(&MyClass::MyFunc, myclass, 23, "helloworld");
cb.Run();

  这里绑定的对象即为myclass,这样的调用方式存在一定的风险,在目标线程调用该对象时无法确保该对象还存在。因此可以使用chromium中的智能指针来绑定相关对象。
  

class MyClass {
public:
    void MyFunc(int i, const std::string& str)
};
scoped_refptr<MyClass> myclass(new MyClass);  
base::Callback<void(void)> cb = base::Bind(&MyClass::MyFunc, myclass, 23, "hello world"); 
cb.Run();

  Callback对象cb使用完成之后,对象myclass会自动释放,这样就可以保证Callback对象cb的成员函数Run被调用时,对象myclass是存在的。 我们甚至还可以通过WeakPtr弱智能指针来绑定类对象。
  

class MyClass {
public:
    void MyFunc(int i, const std::string& str)
};
scoped_refptr<MyClass> myclass(new MyClass);  
base::Callback<void(void)> cb = base::Bind(&MyClass::MyFunc, GetWeakPtr(myclass), 23, "hello world"); 
cb.Run();

  接下来分析chromium中callback对象的创建和调用过程。首先Callback与其他类的类图如下所示
  这里写图片描述
  
  BindState类将宿主Callback对象创建时绑定的参数保存在成员变量p1_、p2_等中,绑定的函数则保存在成员变量runnable_指向的一个RunnableAdapter对象的成员变量function_中。
  当CallbackBase类的成员函数Run被调用时,它们通过成员变量polymorphic_invoke_调用Invoker类的静态成员函数Run。传递给Invoker类的静态成员函数Run的参数包括:
  1. 从父类BindStateBase继承下来的成员变量bind_state_指向的一个BindState对象;
  2. 传递给CallbackBase类的成员函数Run的参数。
   Invoker类的静态成员函数Run从上述的BindState对象分别取出与它绑定的参数p1_, p2_等,以及RunnableAdapter对象,连同传递给CallbackBase类的成员函数Run的参数,一起再传递给InvokeHelper类的静态成员函数MakeItSo。InvokeHelper类的静态成员函数MakeItSo接下来又会调用传递给它的RunnableAdapter对象的成员函数Run,后者又会调用保存在其成员变量function_的函数,这时候调用的实际上就是其关联的Callback对象绑定的函数。
   下面从几个代码片段来分析Callback的创建和调用过程,Bind函数的其中一个定义如下所示
  

template <typename Functor, typename P1>
base::Callback<
    typename internal::BindState<
        typename internal::FunctorTraits<Functor>::RunnableType,
        typename internal::FunctorTraits<Functor>::RunType,
        void(typename internal::CallbackParamTraits<P1>::StorageType)>
            ::UnboundRunType>
Bind(Functor functor, const P1& p1) {
  // Typedefs for how to store and run the functor.
  typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType;
  typedef typename internal::FunctorTraits<Functor>::RunType RunType;

  // Use RunnableType::RunType instead of RunType above because our
  // checks should below for bound references need to know what the actual
  // functor is going to interpret the argument as.
  typedef internal::FunctionTraits<typename RunnableType::RunType>
      BoundFunctorTraits;

  // Do not allow binding a non-const reference parameter. Non-const reference
  // parameters are disallowed by the Google style guide.  Also, binding a
  // non-const reference parameter can make for subtle bugs because the
  // invoked function will receive a reference to the stored copy of the
  // argument and not the original.
  COMPILE_ASSERT(
      !(is_non_const_reference<typename BoundFunctorTraits::A1Type>::value ),
      do_not_bind_functions_with_nonconst_ref);

  // For methods, we need to be careful for parameter 1.  We do not require
  // a scoped_refptr because BindState<> itself takes care of AddRef() for
  // methods. We also disallow binding of an array as the method's target
  // object.
  COMPILE_ASSERT(
      internal::HasIsMethodTag<RunnableType>::value ||
          !internal::NeedsScopedRefptrButGetsRawPtr<P1>::value,
      p1_is_refcounted_type_and_needs_scoped_refptr);
  COMPILE_ASSERT(!internal::HasIsMethodTag<RunnableType>::value ||
                     !is_array<P1>::value,
                 first_bound_argument_to_method_cannot_be_array);
  typedef internal::BindState<RunnableType, RunType,
      void(typename internal::CallbackParamTraits<P1>::StorageType)> BindState;


  return Callback<typename BindState::UnboundRunType>(
      new BindState(internal::MakeRunnable(functor), p1));
}

  可以看出来主要做了三件事情:
  1.调用internal::MakeRunnable创建了一个RunnableAdapter对象。
  2.调用BindState的构造函数,传入两个参数分别为RunnableAdapter对象,p1。
  3.由BindState对象构造一个Callback对象。
  其中MakeRunnable实现如下代码所示:
  

template <typename T>
typename FunctorTraits<T>::RunnableType MakeRunnable(const T& t) {
  return RunnableAdapter<T>(t);
}

  从中我们就可以知道,Chromium里面的Callback机制实际上就是预先通过一系列预定义的C++模板类和C++模板函数来实现的,使得我们可以将一些预先指定的参数和函数绑定在一个BindState对象和一个RunnableAdapter对象中,并且封装在一个Callback对象中,最后以Callback类的成员函数Run作为统一的调用接口来间接地调用预先绑定的函数,并且将预先指定的参数以及调用Callback类的成员函数Run指定的参数传递给它。
  这种Callback机制适合用作线程间异步通信。假设有两个线程A和B,其中线程A希望在线程B中执行一个任务。这时候线程A就可以将该任务封装成一个Callback对象,并且将该Callback对象发送到线程B的任务队列去。于是线程B调度该Callback对象时,不需要知道它描述的是一个什么样的任务,只需要统一地调用它的成员函数Run,就可以让它执行一个具体的任务。
  Chromium将这种用于线程间异步通信的Callback对象称为Closure对象,如下所示:
  

typedef Callback<void(void)> Closure;

  这意味着一个Closure对象是这样的一个Callback对象:

  1.被调用的函数,以及调用该函数时使用的所有参数,都必须预先指定,也就是不能在调用时指定。

  2. 被调用的函数的返回值为void。

  满足上述条件的Callback对象就称为Closure对象,也就是Chromium多线程通信的Closure机制

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值