为了充分利用CPU多核特性,Chromium在启动时会创建很多线程,来负责执行不同的操作。这样就涉及到了多线程通信问题。Chromium为每一个线程都创建了一个消息队列。当一个线程需要另一个线程执行某一操作时,就向该线程的消息队列发送一个Closure。这个Closure最终在目标线程中得到执行。这种基于Closure的多线程通信方式在Chromium中使用得很普通,因此本文就对它的实现进行分析。
Closure从字面翻译就是闭包的意思,但是它与JavaScript或者 C++ 11里面的闭包是不一样的,因为前者不像后者一样,会对作用域中的变量进行引用。Chromium多线程通信用到的Closure实际上是一个特殊的Callback。因此,本文实际要讲的是Chromium的Callback机制。当我们理解了Chromium的Callback机制之后,就自然而然地理解Chromium多线程通信用到的Closure机制。
一个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对象。这个Callback对象与函数MyFunc进行绑定,同时被绑定的还有函数MyFunc被调用时传递进去的第一个整型参数23。我们接着调用了上述Callback对象的成员函数Run,并且给它传递了一个字符串参数"hello world"。这时候实际被调用的是函数MyFunc,并且传递给它的两个参数分别就为整数23和字符串"hello world"。
如果我们把上面创建的Callback对象看作是一个Closure,那么它首先会被发送到目标线程的消息队列中,然后再在目标线程中调用它的成员函数Run,最后就会导致函数MyFunc在目标线程中被调用。
从上面的代码片段我们可以看到一个有趣的东西,那就是函数MyFunc被调用时传递进去的两个参数是分开指定的。其中第一个参数在调用前指定的,而另一个参数是在调用时指定的。实际上我们可以做到所有的参数均在调用前指定,以及所有的参数均在调用时指定。当不是所有的参数均在调用时指定时,我们就可以简化一个函数的调用过程,即对一个函数实现了不同形式的调用,或者说可以使用不同的方式来调用一个函数。
此外,上面的代码片段创建的Callback对象是与一个全局函数绑定的。实际上,一个Callback对象还可以与一个类的成员函数绑定,不过这时候在调用函数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, "hello world");
这里绑定的类对象就为myclass,并且是以裸指针的形式指定的。这时候我们就需要保证Callback对象cb的成员函数Run被调用时,对象myclass还没有被销毁。
为了更方便地管理被绑定对象的生命周期,函数base::Bind允许通过我们在前面Chromium和WebKit的智能指针实现原理解析一文分析的scoped_refptr智能指针来绑定类对象,如下所示:
scoped_refptr<MyClass> myclass(new MyClass);
base::Callback<void(void)> cb = base::Bind(&MyClass::MyFunc, myclass, 23, "hello world");
Callback对象cb使用完成之后,对象myclass会自动释放,这样就可以保证Callback对象cb的成员函数Run被调用时,对象myclass是存在的。
我们甚至还可以通过我们在前面Chromium和WebKit的智能指针实现原理解析一文分析的WeakPtr弱智能指针来绑定类对象,如下所示:
scoped_refptr<MyClass> myclass(new MyClass);
base::Callback<void(void)> cb = base::Bind(&MyClass::MyFunc, GetWeakPtr(myclass), 23, "hello world");
我们假设可以通过函数GetWeakPtr获得对象myclass的一个WeakPtr弱智能指针,这样当Callback对象cb的成员函数Run被调用时,如果对象myclass已经被销毁,那么就不会调用它的成员函数MyFunc。
最后,传递给最终被调用的函数的参数不仅可以是整型和字符串等基本类型,还可以是对象,如下所示:
void TakesOneRef(scoped_refptr<MyClass> arg) {}
scoped_refptr<MyClass> f(new MyClass)
base::Closure cb = base::Bind(&TakesOneRef, f);
传递给被调用函数的对象可以通过裸指针传递,也可以通过在前面Chromium和WebKit的智能指针实现原理解析一文分析的scoped_ptr和scoped_refptr智能指针指定,这样就可以方便地管理被传递对象的生命周期。
接下来,我们就通过源代码来分析Chromium的Callback机制的实现,也就是Callback对象的创建和调用过程。
在分析Callback对象的创建和调用过程之前,我们首先看它的类关系图,如图1所示:
图1 Callback类关系图
从图1可以看到,Callback类是从CallbackBase类继承下来的。CallbackBase类有两个成员变量bind_state_和polymorphicinvoke。其中,成员变量bind_state_指向的是一个BindState对象,该BindState对象是保存了其宿主Callback对象创建时绑定的函数以及参数。另外一个成员变量polymorphic_invoke_指向的是一个InvokeFuncStorage对象,该InvokeFuncStorage对象描述的是Invoker类的静态成员函数Run。
BindState类是从BindStateBase类继承下来的,BindStateBase类又是从RefCountedThreadSafe类继承下来的。从前面Chromium和WebKit的智能指针实现原理分析一文可以知道,从RefCountedThreadSafe继承下来的类可以配合智能指针来使用,因此我们就可以看到CallbackBase类的成员变量bind_state_是一个引用了BindStateBase对象的scoped_refptr智能指针。
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对象绑定的函数。
大概理解了图1所示的Callback类关系图之后,接下来我们将以下面两个代码片段来分析Callback对象的创建和调用过程。
第一个代码片段如下所示:
void MyFunc(int i, const std::string& str) {}
base::Callback<void(const std::string&)> cb = base::Bind(&MyFunc, 23);
cb.Run("hello world");
它调用的模板函数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;
......
typedef internal::BindState<RunnableType, RunType,
void(typename internal::CallbackParamTraits<P1>::StorageType)> BindState;
return Callback<typename BindState::UnboundRunType>(
new BindState(internal::MakeRunnable(functor), p1));
}
这个函数定义在文件chromium_org/base/bind.h中。
第二个代码片段如下所示:
class MyClass {
public:
void MyFunc(int i, const std::string& str) {}
};
MyClass* myclass = new MyClass;
base::Callback<void(const std::string&)> cb = base::Bind(&MyClass::MyFunc, myclass, 23);
cb.Run("hello world");
它调用的模板函数Bind的定义如下所示:
template <typename Functor, typename P1, typename P2>
base::Callback<
typename internal::BindState<
typename internal::FunctorTraits<Functor>::RunnableType,
typename internal::FunctorTraits<Functor>::RunType,
void(typename internal::CallbackParamTraits<P1>::StorageType,
typename internal::CallbackParamTraits<P2>::StorageType)>
::UnboundRunType>
Bind(Functor functor, const P1& p1, const P2& p2) {
// Typedefs for how to store and run the functor.
typedef typename internal::FunctorTraits<Functor>::RunnableType RunnableType;
typedef typename internal::FunctorTraits<Functor>::RunType RunType;
......
typedef internal::BindState<RunnableType, RunType,
void(typename internal::CallbackParamTraits<P1>::StorageType,
typename internal::CallbackParamTraits<P2>::StorageType)> BindState;
return Callback<typename BindState::UnboundRunType>(
new BindState(internal::MakeRunnable(functor), p1, p2));
}
这个函数定义在文件chromium_org/base/bind.h中。
上述两个模板函数Bind做了三件事情:
1. 调用模板函数MakeRunnable创建了一个RunnableAdapter对象;
2. 使用上述对应的RunnableAdapter对象和参数p1创建了一个BindState对象;
3. 使用上述BindState对象创建了一个Callback对象。
接下来我们就要继续分析上述三对象的创建过程,不过在分析之前,我们需要理解这里涉及到的两个模板类FunctorTraits和CallbackParamTraits的定义和作用。
模板类FunctorTraits的定义如下所示:
template <typename T>
struct FunctorTraits {
typedef RunnableAdapter<T> RunnableType;
typedef typename RunnableType::RunType RunType;
};
这个类定义在文件chromium_org/base/bind_internal.h中。
这里的模板参数T描述的是一个函数,例如,对于上述的第一个代码片段,T就为void()(int, const std::string&),而对于第二个代码片段,T就为void(MyClass::)(int, const std::string&),这个模板参数T又作为参数用来定义RunnableAdapter。
当模板参数T等于void(*)(int, const std::string&)时,RunnableAdapter
template <typename R, typename A1, typename A2>
class RunnableAdapter<R(*)(A1, A2)> {
public:
typedef R (RunType)(A1, A2);
explicit RunnableAdapter(R(*function)(A1, A2))
: function_(function) {
}
R Run(typename CallbackParamTraits<A1>::ForwardType a1,
typename CallbackParamTraits<A2>::ForwardType a2) {
return function_(CallbackForward(a1), CallbackForward(a2));
}
private:
R (*function_)(A1, A2);
};
这个类定义在文件external/chromium_org/base/bind_internal.h中。
这时候FunctorTraits
当模板参数T等于为void(MyClass::*)(int, const std::string&)时,RunnableAdapter
template <typename R, typename T, typename A1, typename A2>
class RunnableAdapter<R(T::*)(A1, A2)> {
public:
typedef R (RunType)(T*, A1, A2);
typedef true_type IsMethod;
explicit RunnableAdapter(R(T::*method)(A1, A2))
: method_(method) {
}
R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1,
typename CallbackParamTraits<A2>::ForwardType a2) {
return (object->*method_)(CallbackForward(a1), CallbackForward(a2));
}
private:
R (T::*method_)(A1, A2);
};
这个类定义在文件external/chromium_org/base/bind_internal.h中。
这时候FunctorTraits
从上面的分析就可以看出,模板类FunctorTraits实际上是用来描述一个函数的签名的。
我们再来看模板类CallbackParamTraits的定义,如下所示:
template <typename T, bool is_move_only = IsMoveOnlyType<T>::value>
struct CallbackParamTraits {
typedef const T& ForwardType;
typedef T StorageType;
};
这个类定义在文件chromium_org/base/callback_internal.h中。
模板类CallbackParamTraits不单止在前面的函数Bind用到,在前面列出的两个模板类RunnableAdapter中也有用到,它是用来描述一个函数参数的签名的。例如,对于上述两个代码片段,模板参数T描述的就是一个类型为const std::string的参数。
从模板类CallbackParamTraits的定义还可以看到,CallbackParamTraits::ForwardType描述的是参数的引用类型,而CallbackParamTraits::StorageType描述的是参数的值类型 。例如,对于上述第一个代码片段,CallbackParamTraits
模板类CallbackParamTraits还有一个模板参数is_move_only,它的默认值等于IsMoveOnlyType
IsMoveOnlyType
template <typename T> struct IsMoveOnlyType {
template <typename U>
static YesType Test(const typename U::MoveOnlyTypeForCPP03*);
template <typename U>
static NoType Test(...);
static const bool value = sizeof(Test<T>(0)) == sizeof(YesType) &&
!is_const<T>::value;
};
这个类定义在文件external/chromium_org/base/callback_internal.h中。
模板类IsMoveOnlyType
因此,我们就可以看出,当调用模板类IsMoveOnlyType
从前面Chromium和WebKit的智能指针实现原理分析一文可以知道,当一个类的定义嵌套了宏MOVE_ONLY_TYPE_FOR_CPP_03时,该类就是有具有move语意的,例如智能指针类scoped_ptr就是具有move语意的。其中,宏MOVE_ONLY_TYPE_FOR_CPP_03就为一个类定义了一个内部类型MoveOnlyTypeForCPP03,如下所示:
#define MOVE_ONLY_TYPE_FOR_CPP_03(type, rvalue_type) \
private: \
......
public: \
......
typedef void MoveOnlyTypeForCPP03; \
private:
这个宏定义在文件external/chromium_org/base/move.h中。
回到模板类IsMoveOnlyType
接下来,我们继续看模板类is_const
template <class T> struct is_const : false_type {};
template <class T> struct is_const<const T> : true_type {};
这两个类定义在文件external/chromium_org/base/template_util.h中。
从这里就可以看到,如果模板参数T作为参数传递时,有关键字const修饰,那么模板类is_const
类true_type和false_type的定义如下所示:
template<class T, T v>
struct integral_constant {
static const T value = v;
......
};
typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;
这两个类定义在文件external/chromium_org/base/template_util.h中。
从这里就可以看到,类true_type实际上是一个integral_constant<bool, true>类,它的静态成员变量value的值等于true,而类false_type实际上是一个integral_constant<bool, false>类,它的静态成员变量value的值等于false。
以上就是在模板函数Bind中涉及到的两个模板类FunctorTraits和CallbackParamTraits及其相关的类的定义和作用。有了这些基础知识之后,我们就可以继续分析模板函数Bind创建RunnableAdapter对象、BindState对象和Callback对象的过程了。
我们首先看RunnableAdapter对象的创建过程,它是由以下代码创建的:
internal::MakeRunnable(functor)
也就是调用函数internal::MakeRunnable来创建,其中参数functor描述的是一个被函数Bind绑定的函数。例如,对于上述的第一个代码片段,参数functor描述的函数即为MyFunc,而对于上述的第二个代码片段,参数functor描述的函数即为MyClass::MyFunc。
函数internal::MakeRunnable的定义如下所示:
template <typename T>
typename FunctorTraits<T>::RunnableType MakeRunnable(const T& t) {
return RunnableAdapter<T>(t);
}
这个函数定义在文件external/chromium_org/base/bind_internal.h中。
对于上述的第一个代码片段,参数t描述的函数为MyFunc,它的原型为void()(int, const std::string&),因此函数internal::MakeRunnable创建的RunnableAdapter对象的类型就是前面提到的RunnableAdapter<R()(A1,A2)>,这时候函数MyFunc的地址就保存在其成员变量function_中。
对于上述的第二个代码片段,参数t描述的函数为MyClass::MyFunc,它的原型是类void()(int, const std::string&),因此函数internal::MakeRunnable创建的RunnableAdapter对象的类型就是前面提到的RunnableAdapter<R(T::)(A1,A2)>,这时候MyClass类的成员函数MyFunc的地址就保存在其成员变量function_中。
RunnableAdpater对象创建完成之后,函数Bind接下来就继续创建一个BindState对象。
对于上述的第一个代码片段,BindState对象通过以下的代码来创建:
new BindState(RunnableAdapter<R(*)(A1,A2)>, p1)
这里创建的BindState对象的类型如下所示:
template <typename Runnable, typename RunType, typename P1>
struct BindState<Runnable, RunType, void(P1)> : public BindStateBase {
typedef Runnable RunnableType;
typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
typedef Invoker<1, BindState, RunType> InvokerType;
typedef typename InvokerType::UnboundRunType UnboundRunType;
// Convenience typedefs for bound argument types.
typedef UnwrapTraits<P1> Bound1UnwrapTraits;
BindState(const Runnable& runnable, const P1& p1)
: runnable_(runnable),
p1_(p1) {
MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
}
virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value,
P1>::Release(p1_); }
RunnableType runnable_;
P1 p1_;
};
这个类定义在文件external/chromium_org/base/bind_internal.h中。
从上面的调用过程可以知道,第一个模板参数Runnable的类型为RunnableAdapter<R()(A1, A2)>,这里的R、A1和A2即分别为void、int和const std::string&。第二个参数RunType的类型为R()(A1,A2)。第三个模板参数P1的类型为int。
对于上述的第二个代码片段,BindState对象通过以下的代码来创建:
new BindState(RunnableAdapter<R(T::*)(A1,A2)>, p1, p2)
也就是说,这里创建的BindState对象的类型如下所示:
template <typename Runnable, typename RunType, typename P1, typename P2>
struct BindState<Runnable, RunType, void(P1, P2)> : public BindStateBase {
typedef Runnable RunnableType;
typedef IsWeakMethod<HasIsMethodTag<Runnable>::value, P1> IsWeakCall;
typedef Invoker<2, BindState, RunType> InvokerType;
typedef typename InvokerType::UnboundRunType UnboundRunType;
// Convenience typedefs for bound argument types.
typedef UnwrapTraits<P1> Bound1UnwrapTraits;
typedef UnwrapTraits<P2> Bound2UnwrapTraits;
BindState(const Runnable& runnable, const P1& p1, const P2& p2)
: runnable_(runnable),
p1_(p1),
p2_(p2) {
MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
}
virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value,
P1>::Release(p1_); }
RunnableType runnable_;
P1 p1_;
P2 p2_;
};
这个类定义在文件external/chromium_org/base/bind_internal.h中。
从上面的调用过程可以知道,第一个模板参数Runnable的类型为RunnableAdapter<R(T::)(A1, A2)>,这里的R、A1和A2即分别为void、int和const std::string&。第二个参数RunType的类型为R(T::)(A1,A2)。第三模板参数P1和第四个模板参数P2的类型分别MyClass和int。
在分析上述的两个模板类BindState的实现之前,我们需要先理解在它们内部定义的RunnableType、IsWeakCall、InvokerType、UnboundRunType和Bound1UnwrapTraits类型的含义。
RunnableType定义为Runnable,对于上述第一个代码段,Runnable即RunnableAdapter<R()(A1,A2)>类,而对于上述第二个代码段,Runnable即为RunnableAdapter<R(T::)(A1,A2)>类。
IsWeakCall定义为IsWeakMethod<HasIsMethodTag
template <typename T>
class HasIsMethodTag {
typedef char Yes[1];
typedef char No[2];
template <typename U>
static Yes& Check(typename U::IsMethod*);
template <typename U>
static No& Check(...);
public:
static const bool value = sizeof(Check<T>(0)) == sizeof(Yes);
};
这个类定义在文件external/chromium_org/base/bind_helpers.h中。
模板类HasIsMethodTag
回到上面分析的BindState类中。对于上述的第一个代码段,HasIsMethodTag
对于上述的第二个代码段,HasIsMethodTag
模板类IsWeakMethod有三个定义,如下所示:
template <bool IsMethod, typename P1>
struct IsWeakMethod : public false_type {};
template <typename T>
struct IsWeakMethod<true, WeakPtr<T> > : public true_type {};
template <typename T>
struct IsWeakMethod<true, ConstRefWrapper<WeakPtr<T> > > : public true_type {};
这三个类定义在文件external/chromium_org/base/bind_helpers.h中。
从这里就可以看出,当模板类IsWeakCall指定的第一个模板参数为true,并且第二个模板参数P1是一个WeakPtr弱智能指针时,它就是从类true_type继承下来的。在其余情况下,模板类IsWeakCall是从类false_type继承下来的。
这意味着对于上述的第一个代码段,模板类BindState内部定义的类型IsWeakCall描述的是一个从false_type继承下来的子类,而对于上述的第二个代码段, 模板类BindState内部定义的类型IsWeakCall描述的也是一个从false_type继承下来的子类。
回到上述第一个代码对应的模板类BindState中,它内部定义的类型InvokerType描述的是一个Invoker<1, BindState, RunType>类,定义如下所示:
template <typename StorageType, typename R,typename X1, typename X2>
struct Invoker<1, StorageType, R(X1, X2)> {
typedef R(RunType)(BindStateBase*,
typename CallbackParamTraits<X2>::ForwardType);
typedef R(UnboundRunType)(X2);
static R Run(BindStateBase* base,
typename CallbackParamTraits<X2>::ForwardType x2) {
StorageType* storage = static_cast<StorageType*>(base);
// Local references to make debugger stepping easier. If in a debugger,
// you really want to warp ahead and step through the
// InvokeHelper<>::MakeItSo() call below.
typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
typename Bound1UnwrapTraits::ForwardType x1 =
Bound1UnwrapTraits::Unwrap(storage->p1_);
return InvokeHelper<StorageType::IsWeakCall::value, R,
typename StorageType::RunnableType,
void(typename Bound1UnwrapTraits::ForwardType,
typename CallbackParamTraits<X2>::ForwardType x2)>
::MakeItSo(storage->runnable_, CallbackForward(x1),
CallbackForward(x2));
}
};
这个类定义在文件external/chromium_org/base/bind_internal.h中。
模板参数1表示这里定义的Invoker类绑定了一个参数,该参数的类型由第一个模板参数X1定义。第二个模板参数StorageType描述的是一个BindState类型。从前面的调用过程可以知道,模板参数X1的类型为int。
Invoker<1, BindState, RunType>类的成员函数Run的实现我们后面再分析。
现在回到上述第二个代码段对应的模板类BindState中,它内部定义的类型InvokerType描述的是一个Invoker<2, BindState, RunType>类,定义如下所示:
template <typename StorageType, typename R,typename X1, typename X2,
typename X3>
struct Invoker<2, StorageType, R(X1, X2, X3)> {
typedef R(RunType)(BindStateBase*,
typename CallbackParamTraits<X3>::ForwardType);
typedef R(UnboundRunType)(X3);
static R Run(BindStateBase* base,
typename CallbackParamTraits<X3>::ForwardType x3) {
StorageType* storage = static_cast<StorageType*>(base);
// Local references to make debugger stepping easier. If in a debugger,
// you really want to warp ahead and step through the
// InvokeHelper<>::MakeItSo() call below.
typedef typename StorageType::Bound1UnwrapTraits Bound1UnwrapTraits;
typedef typename StorageType::Bound2UnwrapTraits Bound2UnwrapTraits;
typename Bound1UnwrapTraits::ForwardType x1 =
Bound1UnwrapTraits::Unwrap(storage->p1_);
typename Bound2UnwrapTraits::ForwardType x2 =
Bound2UnwrapTraits::Unwrap(storage->p2_);
return InvokeHelper<StorageType::IsWeakCall::value, R,
typename StorageType::RunnableType,
void(typename Bound1UnwrapTraits::ForwardType,
typename Bound2UnwrapTraits::ForwardType,
typename CallbackParamTraits<X3>::ForwardType x3)>
::MakeItSo(storage->runnable_, CallbackForward(x1),
CallbackForward(x2), CallbackForward(x3));
}
};
这个类定义在文件external/chromium_org/base/bind_internal.h中。
模板参数2表示这里定义的Invoker类绑定了两个参数,第一个参数的类型由模板参数X1定义,第二个参数的类型由模板参数X2定义。第二个模板参数StorageType描述的是一个BindState类型。从前面的调用过程可以知道,模板参数X1的类型为MyClass,而模板参数X2的类型为int。
Invoker<2, BindState, RunType>类的成员函数Run的实现我们同样后面再分析。
现在回到上述第一个代码段对应的模板类BindState中,它内部定义的类型UnboundRunType定义为Invoker<1, BindState, RunType>::UnboundRunType,它的定义如下所示:
typedef R(UnboundRunType)(X2);
也就是它描述的是一个参数类型为X2的函数,其中,X2是模板类Invoker<1, BindState, RunType>的模板参数,定义为const std::string&。
另外,上述第一个代码段对应的模板类BindState内部定义的类型Bound1UnwrapTraits定义为UnwrapTraits
模板类UnwrapTraits有若干个定义,如下所示:
template <typename T>
struct UnwrapTraits {
typedef const T& ForwardType;
static ForwardType Unwrap(const T& o) { return o; }
};
template <typename T>
struct UnwrapTraits<UnretainedWrapper<T> > {
typedef T* ForwardType;
static ForwardType Unwrap(UnretainedWrapper<T> unretained) {
return unretained.get();
}
};
template <typename T>
struct UnwrapTraits<ConstRefWrapper<T> > {
typedef const T& ForwardType;
static ForwardType Unwrap(ConstRefWrapper<T> const_ref) {
return const_ref.get();
}
};
template <typename T>
struct UnwrapTraits<scoped_refptr<T> > {
typedef T* ForwardType;
static ForwardType Unwrap(const scoped_refptr<T>& o) { return o.get(); }
};
template <typename T>
struct UnwrapTraits<WeakPtr<T> > {
typedef const WeakPtr<T>& ForwardType;
static ForwardType Unwrap(const WeakPtr<T>& o) { return o; }
};
template <typename T>
struct UnwrapTraits<OwnedWrapper<T> > {
typedef T* ForwardType;
static ForwardType Unwrap(const OwnedWrapper<T>& o) {
return o.get();
}
};
template <typename T>
struct UnwrapTraits<PassedWrapper<T> > {
typedef T ForwardType;
static T Unwrap(PassedWrapper<T>& o) {
return o.Pass();
}
};
这些类定义在文件external/chromium_org/base/bind_helpers.h中。
它们的作用实际上就是定义一个静态成员函数Unwrap,用来被绑定参数的值。由于被绑定参数的类型不一样,获取它们的值的方式也是不一样的。例如,如果被绑定的是一个scoped_refptr智能指针,那么我们实际上需要的是该智能指针引用的目标对象,这时候就可以通过调用该智能指针的成员函数get来获得,如模板类UnwrapTraits<scoped_refptr
现在回到上述第二个代码段对应的模板类BindState中,它内部定义的类型UnboundRunType定义为Invoker<2, BindState, RunType>::UnboundRunType,它的定义如下所示:
typedef R(UnboundRunType)(X3);
也就是它描述的是一个参数类型为X2的函数,其中,X2是模板类Invoker<1, BindState, RunType>的模板参数,定义为const std::string&。
另外,上述第二个代码段对应的模板类BindState内部定义的类型Bound1UnwrapTraits和Bound2UnwrapTraits分别定义为UnwrapTraits
有了上面的基础知识之后,接下来我们就来看上述第一个代码段对应的BindState对象的创建过程,也就是它的构造函数的实现,如下所示:
BindState(const Runnable& runnable, const P1& p1)
: runnable_(runnable),
p1_(p1) {
MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
}
根据我们前面的分析,参数runnable指向的是一个RunnableAdapter<R(*)(A1,A2)>,而参数p1描述的是一个int。BindState类的构造函数分别将这两个参数分别保存在成员变量runnable和p1。
此外,BindState类的构造函数还会通过模板类MaybeRefCount判断是否需要增加参数p1描述的对象的引用计数。这主要是针对被绑定函数为类成员函数的情况进行处理的。如果我们在创建一个Callback对象时,绑定的函数是一个类成员函数时,那么这里的参数p1就必须要是一个对应的类对象,因为当被绑定的类成员函数被调用时,这个类对象是作为this指针处理的。当这个类对象是以裸指针的形式传递给模板函数Bind时,需要增加它的引用计数,使得它的生命周期不短于对应的Callback对象。
由于这里的参数p1是一个int,从前面的分析可以知道,HasIsMethodTag
MaybeRefcount是一个模板类,它有若干个定义,如下所示:
template <typename T>
struct MaybeRefcount<false, T> {
static void AddRef(const T&) {}
static void Release(const T&) {}
};
template <typename T, size_t n>
struct MaybeRefcount<false, T[n]> {
static void AddRef(const T*) {}
static void Release(const T*) {}
};
template <typename T>
struct MaybeRefcount<true, T> {
static void AddRef(const T&) {}
static void Release(const T&) {}
};
template <typename T>
struct MaybeRefcount<true, T*> {
static void AddRef(T* o) { o->AddRef(); }
static void Release(T* o) { o->Release(); }
};
// No need to additionally AddRef() and Release() since we are storing a
// scoped_refptr<> inside the storage object already.
template <typename T>
struct MaybeRefcount<true, scoped_refptr<T> > {
static void AddRef(const scoped_refptr<T>& o) {}
static void Release(const scoped_refptr<T>& o) {}
};
template <typename T>
struct MaybeRefcount<true, const T*> {
static void AddRef(const T* o) { o->AddRef(); }
static void Release(const T* o) { o->Release(); }
};
这些类定义在文件external/chromium_org/base/bind_helpers.h中。
从这里就可以看到,当第一个模板参数等于false时,MaybeRefcount类的静态成员函数AddRef和Release什么也不做,因为这时候意味着它们的参数描述的不是一个类对象。
另一方面,当第一个模板参数等于true,并且第二个模板参数描述的是一个类对象的裸指针时,MaybeRefcount类的静态成员函数AddRef和Release会对该对象的引用计数进行加1和减1的操作。如我们前面所述,这时候MaybeRefcount类的静态成员函数AddRef和Release的参数描述的对象是作为被绑定的类成员函数调用时的this指针处理的,因此要通过操作它的引用计数来正确管理它的生命周期。
这里有两点是需要注意的。第一点是如果第二个模板参数描述的是一个scoped_refptr智能指针时,那么对应的MaybeRefcount类是不需要增加或者减少它引用的对象的引用计数的,因为这时候这个被引用的对象也是通过scoped_refptr智能指针绑定在对应的BindState对象里面的,也就是scoped_refptr智能指针会自动处理好被引用对象的引用计数。
第二点是如果描述的是一个WeakPtr弱智能指针时,那么对应的MaybeRefcount类也不会增加或者减少它引用的对象的引用计数。这是因为当一个对象通过WeakPtr弱智能指针绑定到一个Callback对象时,它的生命周期可以短于对应的Callback对象。当发生这种情况时,对应的Callback对象的成员函数Run被调用时,之前绑定的类成员函数就不会被调用。
由于在BindState类的构造函数中有可能增加了参数p1描述的对象的引用计数,因此在BindState类的析构函数中,需要减少该对象的引用计数,如下所示:
virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value,
P1>::Release(p1_); }
以上就是上述第一个代码段对应的BindState对象的创建过程,对于第二个代码段对应的BindState对象的创建过程,也是类似的,如下所示:
BindState(const Runnable& runnable, const P1& p1, const P2& p2)
: runnable_(runnable),
p1_(p1),
p2_(p2) {
MaybeRefcount<HasIsMethodTag<Runnable>::value, P1>::AddRef(p1_);
}
注意,这里只有参数p1有可能是作为this指针处理的,因此在BindState类的构造函数中,只会调用模板类MaybeRefcount的静态成员函数AddRef对它进行处理,另外一个参数p2就没有这样的待遇。
同样的,当创建出来的BindState对象析构时,可能减少参数p1描述的对象的引用计数,如下所示:
virtual ~BindState() { MaybeRefcount<HasIsMethodTag<Runnable>::value,
P1>::Release(p1_); }
这样,BindSate对象的创建过程就分析完成了,回到模板函数Bind中,接下来就以创建出来的BindSate对象为参数,创建最终的Callback对象。
对于上述的第一个代码段,创建Callback对象的代码如下所示:
return Callback<typename BindState::UnboundRunType>(
BindState<RunnableAdapter<R(*)(A1,A2)>, R(*)(A1,A2),void(P1)>);
从前面的分析可以知道,这里的BindState::UnboundRunType的定义如下所示:
template <typename StorageType, typename R,typename X1, typename X2>
struct Invoker<1, StorageType, R(X1, X2)> {
......
typedef R(UnboundRunType)(X2);
......
};
这里的模板参数X2的类型为const std::string&。
对于上述的第二个代码段,创建Callback对象的代码如下所示:
return Callback<typename BindState::UnboundRunType>(
BindState<RunnableAdapter<R(T::*)(A1,A2), R(T::*)(A1,A2), void(P1,P2)>>);
从前面的分析可以知道,这里的BindState::UnboundRunType的定义如下所示:
template <typename StorageType, typename R,typename X1, typename X2,
typename X3>
struct Invoker<2, StorageType, R(X1, X2, X3)> {
......
typedef R(UnboundRunType)(X3);
......
};
这里的模板参数X3的类型为const std::string&。
因此,上述两个代码段创建的Callback对象对应的Callback模板类的定义如下所示:
template <typename R, typename A1>
class Callback<R(A1)> : public internal::CallbackBase {
public:
......
template <typename Runnable, typename BindRunType, typename BoundArgsType>
Callback(internal::BindState<Runnable, BindRunType,
BoundArgsType>* bind_state)
: CallbackBase(bind_state) {
......
PolymorphicInvoke invoke_func =
&internal::BindState<Runnable, BindRunType, BoundArgsType>
::InvokerType::Run;
polymorphic_invoke_ = reinterpret_cast<InvokeFuncStorage>(invoke_func);
}
......
R Run(typename internal::CallbackParamTraits<A1>::ForwardType a1) const {
PolymorphicInvoke f =
reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
return f(bind_state_.get(), internal::CallbackForward(a1));
}
private:
typedef R(*PolymorphicInvoke)(
internal::BindStateBase*,
typename internal::CallbackParamTraits<A1>::ForwardType);
};
这个类定义在文件external/chromium_org/base/callback.h中。
我们观察Callback类的构造函数的实现,它的参数bind_state描述的BindState对象被传递给父类CallbackBase的构造函数,最终保存在父类CallbackBase的成员变量bind_state_中。
从前面的分析可以知道,对于上述第一个代码段,internal::BindState<Runnable, BindRunType, BoundArgsType>::InvokerType::Run描述的函数为以下函数:
template <typename StorageType, typename R,typename X1, typename X2>
struct Invoker<1, StorageType, R(X1, X2)> {
......
static R Run(BindStateBase* base,
typename CallbackParamTraits<X2>::ForwardType x2) {
StorageType* storage = static_cast<StorageType*>(base);
......
typename Bound1UnwrapTraits::ForwardType x1 =
Bound1UnwrapTraits::Unwrap(storage->p1_);
return InvokeHelper<StorageType::IsWeakCall::value, R,
typename StorageType::RunnableType,
void(typename Bound1UnwrapTraits::ForwardType,
typename CallbackParamTraits<X2>::ForwardType x2)>
::MakeItSo(storage->runnable_, CallbackForward(x1),
CallbackForward(x2));
}
};
这个函数定义在文件external/chromium_org/base/bind_internal.h中。
对于上述第二个代码段,internal::BindState<Runnable, BindRunType, BoundArgsType>::InvokerType::Run描述的函数则为以下函数:
template <typename StorageType, typename R,typename X1, typename X2,
typename X3>
struct Invoker<2, StorageType, R(X1, X2, X3)> {
......
static R Run(BindStateBase* base,
typename CallbackParamTraits<X3>::ForwardType x3) {
StorageType* storage = static_cast<StorageType*>(base);
......
typename Bound1UnwrapTraits::ForwardType x1 =
Bound1UnwrapTraits::Unwrap(storage->p1_);
typename Bound2UnwrapTraits::ForwardType x2 =
Bound2UnwrapTraits::Unwrap(storage->p2_);
return InvokeHelper<StorageType::IsWeakCall::value, R,
typename StorageType::RunnableType,
void(typename Bound1UnwrapTraits::ForwardType,
typename Bound2UnwrapTraits::ForwardType,
typename CallbackParamTraits<X3>::ForwardType x3)>
::MakeItSo(storage->runnable_, CallbackForward(x1),
CallbackForward(x2), CallbackForward(x3));
}
};
这个函数定义在文件external/chromium_org/base/bind_internal.h中。
这意味着,对于上述第一个代码段,创建出来的Callback对象从父类CallbackBase继承下来的成员变量polymorphic_invoke_描述的是模板类Invoker<1, StorageType, R(X1, X2)>的成员函数Run,而对于上述第二个代码段,该成员变量描述的是模板类Invoker<2, StorageType, R(X1, X2, X3)>的成员函数Run。
这样,模板函数Bind创建Callback对象的过程也分析完成了。接下来,我们就继续分析这些Callback对象的执行过程,也就是模板类Callback<R(A1)>的成员函数Run的调用过程,如下所示:
template <typename R, typename A1>
class Callback<R(A1)> : public internal::CallbackBase {
public:
......
R Run(typename internal::CallbackParamTraits<A1>::ForwardType a1) const {
PolymorphicInvoke f =
reinterpret_cast<PolymorphicInvoke>(polymorphic_invoke_);
return f(bind_state_.get(), internal::CallbackForward(a1));
}
......
};
这个函数定义在文件external/chromium_org/base/callback.h中。
从前面的分析可以知道,模板类Callback<R(A1)>的成员变量polymorphic_invoke_是从父类CallbackBase继承下来的,对于上述的第一个代码段,它指向的是模板类Invoker<1, StorageType, R(X1, X2)>的静态成员函数Run,而对于上述的第二个代码段,它指向的是模板类Invoker<2, StorageType, R(X1, X2, X3)>的静态成员函数Run。
因此,模板类Callback<R(A1)>的成员函数Run所做的事件就是调用模板类Invoker<1, StorageType, R(X1, X2)>或者Invoker<2, StorageType, R(X1, X2, X3)>的静态成员函数Run,并且将成员变量bind_state_描述的一个BindState对象以及参数a1描述的一个std::string传递给它。
接下来我们就先分析模板类Invoker<1, StorageType, R(X1, X2)>的静态成员函数Run的执行过程,接着再分析模板类Invoker<2, StorageType, R(X1, X2, X3)>的静态成员函数Run的执行过程。
模板类Invoker<1, StorageType, R(X1, X2)>类的静态成员函数Run首先通过前面分析过的Bound1UnwrapTraits类的静态成员函数Unwrap将绑定在传递给它的BindState的参数p1_和runnable_取出来,然后连同传递给它的参数x2,一起传递给以下这个模板类的静态成员函数MakeItSo:
InvokeHelper<StorageType::IsWeakCall::value, R,
typename StorageType::RunnableType,
void(typename Bound1UnwrapTraits::ForwardType,
typename CallbackParamTraits<X2>::ForwardType x2)>
这里的模板参数StorageType描述的是一个BindState<Runnable, RunType, void(P1)>模板类,从前面的分析可以知道,它的内部类IsWeakCall的静态成员变量value的值是等于false的,因此上述InvokeHelper类可以展开为:
InvokeHelper<false, R,
typename StorageType::RunnableType,
void(typename Bound1UnwrapTraits::ForwardType,
typename CallbackParamTraits<X2>::ForwardType x2)>
这里的模板参数R的类型为void,因此上述InvokeHelper类继续展开为:
InvokeHelper<false, void,
typename StorageType::RunnableType,
void(typename Bound1UnwrapTraits::ForwardType,
typename CallbackParamTraits<X2>::ForwardType x2)>
从前面的分析可以知道,模板参数StorageType的内部类RunnableType描述的是一个RunnableAdapter<R(*)(A1, A2)>类,因此上述InvokeHelper类继续展开为:
InvokeHelper<false, void,
RunnableAdapter<R(*)(A1, A2)>,
void(typename Bound1UnwrapTraits::ForwardType,
typename CallbackParamTraits<X2>::ForwardType x2)>
从前面的分析还可以知道,Bound1UnwrapTraits::ForwardType描述的是一个int&,并且CallbackParamTraits
InvokeHelper<false, void,
RunnableAdapter<R(*)(A1, A2)>,
void(int&,const std::string&)>
它对应的模板类的定义为:
template <typename Runnable,typename A1, typename A2>
struct InvokeHelper<false, void, Runnable,
void(A1, A2)> {
static void MakeItSo(Runnable runnable, A1 a1, A2 a2) {
runnable.Run(CallbackForward(a1), CallbackForward(a2));
}
};
这个类定义在文件external/chromium_org/base/bind_internal.h中。
这个InvokeHelper类的静态成员函数MakeItSo最终调用的是参数runnable描述的一个RunnableAdapter<R(*)(A1, A2)>对象的成员函数Run,并且将参数a1和a2传递给它,如下所示:
template <typename R, typename A1, typename A2>
class RunnableAdapter<R(*)(A1, A2)> {
public:
......
R Run(typename CallbackParamTraits<A1>::ForwardType a1,
typename CallbackParamTraits<A2>::ForwardType a2) {
return function_(CallbackForward(a1), CallbackForward(a2));
}
......
};
这个函数定义在文件external/chromium_org/base/bind_internal.h中。
从前面的分析可以知道,这个RunnableAdapter<R(*)(A1, A2)>类的成员变量function_描述的就是上述第一个代码段的函数MyFunc,因此最终就调用了函数MyFunc,并且将参数a1和a2传递给它。注意,这里的参数a1即为在创建当前正在执行的Callback对象时绑定的一个int值,而参数a2即为调用当前正在执行的Callback对象的成员函数Run指定的一个 std::string。
以上就是模板类Invoker<1, StorageType, R(X1, X2)>的静态成员函数Run的执行过程,接下来我们继续分析Invoker<2, StorageType, R(X1, X2, X3)>的静态成员函数Run的执行过程。
模板类Invoker<2, StorageType, R(X1, X2, X3)>类的静态成员函数Run首先通过前面分析过的Bound1UnwrapTraits类和Bound2UnwrapTraits的静态成员函数Unwrap将绑定在传递给它的BindState的参数p1_、p2_和runnable_取出来,然后连同传递给它的参数x3,一起传递给以下这个模板类的静态成员函数MakeItSo:
InvokeHelper<StorageType::IsWeakCall::value, R,
typename StorageType::RunnableType,
void(typename Bound1UnwrapTraits::ForwardType,
typename Bound2UnwrapTraits::ForwardType,
typename CallbackParamTraits<X3>::ForwardType x3)>
这里的模板参数StorageType描述的是一个BindState<Runnable, RunType, void(P1,P2)>模板类,从前面的分析可以知道,它的内部类IsWeakCall的静态成员变量value的值是等于false的,因此上述InvokeHelper类可以展开为:
InvokeHelper<false, R,
typename StorageType::RunnableType,
void(typename Bound1UnwrapTraits::ForwardType,
typename Bound2UnwrapTraits::ForwardType,
typename CallbackParamTraits<X3>::ForwardType x3)>
这里的模板参数R同样也为void,因此上述InvokeHelper类继续展开为:
InvokeHelper<false, void,
typename StorageType::RunnableType,
void(typename Bound1UnwrapTraits::ForwardType,
typename Bound2UnwrapTraits::ForwardType,
typename CallbackParamTraits<X3>::ForwardType x3)>
从前面的分析可以知道,模板参数StorageType的内部类RunnableType描述的是一个RunnableAdapter<R(T::*)(A1, A2)>类,因此上述InvokeHelper类继续展开为:
InvokeHelper<false, void,
RunnableAdapter<R(T::*)(A1, A2)>,
void(typename Bound1UnwrapTraits::ForwardType,
typename Bound2UnwrapTraits::ForwardType,
typename CallbackParamTraits<X3>::ForwardType x3)>
从前面的分析还可以知道,Bound1UnwrapTraits::ForwardType描述的是一个MyClass*,Bound2UnwrapTraits::ForwardType描述的是一个int&,并且CallbackParamTraits
InvokeHelper<false, void,
RunnableAdapter<R(T::*)(A1, A2)>,
void(MyClass*,
int&,
const std::string&)>
它对应的模板类的定义为:
template <typename Runnable,typename A1, typename A2, typename A3>
struct InvokeHelper<false, void, Runnable,
void(A1, A2, A3)> {
static void MakeItSo(Runnable runnable, A1 a1, A2 a2, A3 a3) {
runnable.Run(CallbackForward(a1), CallbackForward(a2), CallbackForward(a3));
}
};
这个类定义在文件external/chromium_org/base/bind_internal.h中。
这个InvokeHelper类的静态成员函数MakeItSo最终调用的是参数runnable描述的一个RunnableAdapter<R(T::*)(A1, A2)>对象的成员函数Run,并且将参数a1、a2和a3传递给它,如下所示:
template <typename R, typename T, typename A1, typename A2>
class RunnableAdapter<R(T::*)(A1, A2)> {
public:
......
R Run(T* object, typename CallbackParamTraits<A1>::ForwardType a1,
typename CallbackParamTraits<A2>::ForwardType a2) {
return (object->*method_)(CallbackForward(a1), CallbackForward(a2));
}
......
};
这个函数定义在文件external/chromium_org/base/bind_internal.h中。
从前面的分析可以知道,这个RunnableAdapter<R(T::*)(A1, A2)>类的成员变量method_描述的就是上述第一个代码段的MyClass类的成员函数MyFunc,因此最终就调用了参数object描述的一个MyClass对象的成员函数MyFunc,并且将参数a1和a2传递给它。注意,这里的参数a1即为在创建当前正在执行的Callback对象时绑定的一个int值,而参数a2即为调用当前正在执行的Callback对象的成员函数Run指定的一个string。
以上就是模板类Invoker<2, StorageType, R(X1, X2, X3)>的静态成员函数Run的执行过程,也就是上述第二个代码段创建的Callback对象的执行过程。
如果我们将第二个代码段修改为:
MyClass* myclass = new MyClass;
base::Callback<void(const std::string&)> cb = base::Bind(&MyClass::MyFunc, GetWeakPtr(myclass), 23);
cb.Run("hello world");
也就是通过WeakPtr弱智能指针将一个MyClass对象绑定在一个Callback对象中,这时候即会导致中间使用的InvokeHepler类的定义如下所示:
template <typename Runnable, typename BoundWeakPtr, typename A2, typename A3>
struct InvokeHelper<true, void, Runnable,
void(BoundWeakPtr, A2, A3)> {
static void MakeItSo(Runnable runnable, BoundWeakPtr weak_ptr, A2 a2, A3 a3) {
if (!weak_ptr.get()) {
return;
}
runnable.Run(weak_ptr.get(), CallbackForward(a2), CallbackForward(a3));
}
};
这个类定义在文件external/chromium_org/base/bind_internal.h中。
从这里可以看到,如果通过弱智能指针引用的对象已经被销毁,也就是调用参数weak_ptr描述的一个WeakPtr弱智能指针的成员函数get的返回值等于0,那么上述InvokeHepler类的静态成员函数MakeItSo就什么也不做就返回了。这意味着,我们在创建一个Callback对象时,如果绑定的是一个类成员函数,并且绑定的类对象是通过WeakPtr弱智能指针引用时,那么当该Callback对象被执行时,之前绑定的类对象已经被销毁,那么被绑定的类成员函数不会被执行。
这样,我们就通过两个例子,分析完成了Callback对象的执行过程,也就是它们的成员函数Run的调用过程。从中我们就可以知道,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;
这个类型定义在文件external/chromium_org/base/callback.h。
这意味着一个Closure对象是这样的一个Callback对象:
1. 被调用的函数,以及调用该函数时使用的所有参数,都必须预先指定,也就是不能在调用时指定。
2. 被调用的函数的返回值为void。
满足上述条件的Callback对象就称为Closure对象,也就是Chromium多线程通信的Closure机制,这与Java的Runnable接口有异工同曲之意。理解了Chromium的Closure机制之后,接下来我们就可以继续分析Chromium的多线程编程模型了,敬请关注!更多的信息也可以关注老罗的新浪微博:http://weibo.com/shengyangluo。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8