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机制