在前面介绍的Chrome Task类,可以让代码在指定线程上运行。 另一种常见的场景就是发出一个异步请求,并想知道请求处理的结果。这时请求的处理过程虽然是在另一个线程上的,但是请求的结果却从(调用者)请求发起的线程上回来,并且请求是可以取消的。 这是很有用的,比如我们经常从界面上(UI线程)发起一个动作,该动作会在Worker线程执行,结束后会通过回调函数回来。这时的回调函数是运行在Work线程上的,直接操作界面是不行的(比如MFC对象就不允许跨线程访问),访问界面的成员变量也可能跟UI线程冲突(这时就必须使用Lock)。 最好呢,我们能转换一下线程,让回调函数在UI线程上运行,这样就可以避免上面说到的问题了。
有了前面的Task基础,我们可以简单设想一下基本的实现方式。 请求发起时,记住当前的线程。然后,在请求完成后,往保存的线程里PastTask让其执行回调函数。 然后,我们来看一下Chrome的实现代码。
使用场景
如下,MyClass对象调用了Frontend 对象的StarRequest方法,参数为some_input1和some_input2,以及回调函数为RequestComplete,callback_consumer_用于跟踪所有发出的请求。一旦MyClass被释放,则未完成的请求就会自动取消了。
class MyClass {
void MakeRequest() {
frontend_service->StartRequest(some_input1, some_input2, callback_consumer_,
NewCallback(this, &MyClass:RequestComplete));
}
void RequestComplete(int status) {
...
}
private:
CallbackConsumer callback_consumer_;
};
Frontend::StartRequest()首先创建一个CancelableRequest对象用于保存回调函数,然后记录该请求对象request和请求者对象consumer,最后是向后台线程放置一个Task(Backend::DoRequest)来处理该请求,参数为request对象,和MyClass传入的some_input1, some_input2参数。这时候,函数返回了,只待结果异步返回了。
class Frontend : public CancelableRequestProvider {
typedef Callback1<int>::Type RequestCallbackType;
Handle StartRequest(int some_input1, int some_input2,
CallbackConsumer* consumer,
RequestCallbackType* callback) {
scoped_refptr<CancelableRequest<RequestCallbackType> > request(
new CancelableRequest<RequestCallbackType>(callback));
AddRequest(request, consumer);
// Send the parameters and the request to the backend thread.
backend_thread_->PostTask(FROM_HERE,
NewRunnableMethod(backend_, &Backend::DoRequest, request,
some_input1, some_input2));
// The handle will have been set by AddRequest.
return request->handle();
}
};
Backend::DoRequest的参数包括了CancelableRequest对象,调用ForwardResult方法即可把结果放回到调用者线程上(即在调用者线程里进行回调)。并且,在执行之前或者执行的过程中可以随时检查请求是否已经取消,如果是则直接退出了。
class Backend {
void DoRequest(
scoped_refptr< CancelableRequest<Frontend::RequestCallbackType> >
request,
int some_input1, int some_input2) {
if (request->canceled())
return;
... do your processing ...
// Depending on your typedefs, one of these two forms will be more
// convenient:
request->ForwardResult(Tuple1<int>(return_value));
// -- or -- (inferior in this case)
request->ForwardResult(Frontend::RequestCallbackType::TupleType(
return_value));
}
};
能够提供CancelableRequest的对象叫做CancelableRequestProvider,其可以跟踪Request的执行。AddRequest方法用于加入一个Request并返回一个Handle,可以用这个Handle取消Request,当Request执行结束也会通过调用RequestCompleted并传入对应的Handle。
class CancelableRequestProvider {
public:
// Identifies a specific request from this provider.
typedef int Handle;
CancelableRequestProvider();
virtual ~CancelableRequestProvider();
// Called by the enduser of the request to cancel it. This MUST be called on
// the same thread that originally issued the request (which is also the same
// thread that would have received the callback if it was not canceled).
void CancelRequest(Handle handle);
protected:
// Adds a new request and initializes it. This is called by a derived class
// to add a new request. The request's Init() will be called (which is why
// the consumer is required. The handle to the new request is returned.
Handle AddRequest(CancelableRequestBase* request,
CancelableRequestConsumerBase* consumer);
// Called by the CancelableRequest when the request has executed. It will
// be removed from the list of pending requests (as opposed to canceling,
// which will also set some state on the request).
void RequestCompleted(Handle handle);
CancelableRequestProvider::AddRequest内部会为request生成一个Handle(其实就是整型数),并通知consumer已经为它创建了一个Request以及对应Handle。同样的,在Request取消和结束的时候,也都会通知consumber。同时,调用request的Init方法完成最后的初始化工作,并传入handle和consumer对象。
CancelableRequestProvider::Handle CancelableRequestProvider::AddRequest(
CancelableRequestBase* request,
CancelableRequestConsumerBase* consumer) {
Handle handle;
{
AutoLock lock(pending_request_lock_);
handle = next_handle_;
pending_requests_[next_handle_] = request;
++next_handle_;
}
consumer->OnRequestAdded(this, handle);
request->Init(this, handle, consumer);
return handle;
}
CancelableRequestConsumer可以作为类的成员变量,这样当对象被释放时,CancelableRequestConsumer自动取消所有进行中的Request,避免回调时崩溃。当然,每次创建一个Request的时候,也需要传入CancelableRequestConsumer对象,这样才能把Request和CancelableRequestConsumer进行绑定。SetClientData和GetClientData可以给每个Request绑定一个数据(类型为T),这样当Request结束和取消的时候就可以比较容易的知道上下文。
class CancelableRequestConsumerBase {
protected:
friend class CancelableRequestProvider;
virtual ~CancelableRequestConsumerBase() {
}
// Adds a new request to the list of requests that are being tracked. This
// is called by the provider when a new request is created.
virtual void OnRequestAdded(CancelableRequestProvider* provider,
CancelableRequestProvider::Handle handle) = 0;
// Removes the given request from the list of pending requests. Called
// by the CancelableRequest immediately after the callback has executed for a
// given request, and by the provider when a request is canceled.
virtual void OnRequestRemoved(CancelableRequestProvider* provider,
CancelableRequestProvider::Handle handle) = 0;
};
template<class T>
class CancelableRequestConsumerTSimple : public CancelableRequestConsumerBase {
public:
CancelableRequestConsumerTSimple() {
}
// Cancel any outstanding requests so that we do not get called back after we
// are destroyed. As these requests are removed, the providers will call us
// back on OnRequestRemoved, which will then update the list. To iterate
// successfully while the list is changing out from under us, we make a copy.
virtual ~CancelableRequestConsumerTSimple() {
CancelAllRequests();
}
// Associates some random data with a specified request. The request MUST be
// outstanding, or it will assert. This is intended to be called immediately
// after a request is issued.
void SetClientData(CancelableRequestProvider* p,
CancelableRequestProvider::Handle h,
T client_data) {
PendingRequest request(p, h);
DCHECK(pending_requests_.find(request) != pending_requests_.end());
pending_requests_[request] = client_data;
}
// Retrieves previously associated data for a specified request. The request
// MUST be outstanding, or it will assert. This is intended to be called
// during processing of a callback to retrieve extra data.
T GetClientData(CancelableRequestProvider* p,
CancelableRequestProvider::Handle h) {
PendingRequest request(p, h);
DCHECK(pending_requests_.find(request) != pending_requests_.end());
return pending_requests_[request];
}
也可以统一给所有的Request带上一个初值,而不是每次赋值太麻烦。CancelableRequestConsumer则是最简单的,啥都不带的。
template<class T, T initial_t>
class CancelableRequestConsumerT : public CancelableRequestConsumerTSimple<T> {
protected:
virtual T get_initial_t() const {
return initial_t;
}
};
typedef CancelableRequestConsumerT<int, 0> CancelableRequestConsumer;
从CancelableRequestBase我们看到,构造时保留了当前线程的MessageLoop,留着待会儿回调用。
class CancelableRequestBase
: public base::RefCountedThreadSafe<CancelableRequestBase> {
CancelableRequestBase()
: provider_(NULL),
consumer_(NULL),
handle_(0),
canceled_(false) {
callback_thread_ = MessageLoop::current();
}
// Tells the provider that the request is complete, which then tells the
// consumer.
void NotifyCompleted() const {
provider_->RequestCompleted(handle());
}
然后看一下具体实现类,其ForwardResult方法就是把结果放到正确的线程上返回,其实就是PostTask,并在回调完毕后通知Provider说Request已经结束。
template<typename CB>
class CancelableRequest : public CancelableRequestBase {
public:
void ForwardResult(const TupleType& param) {
DCHECK(callback_.get());
if (!canceled()) {
if (callback_thread_ == MessageLoop::current()) {
// We can do synchronous callbacks when we're on the same thread.
ExecuteCallback(param);
} else {
callback_thread_->PostTask(FROM_HERE, NewRunnableMethod(this,
&CancelableRequest<CB>::ExecuteCallback, param));
}
}
}
private:
// Executes the callback and notifies the provider and the consumer that this
// request has been completed. This must be called on the callback_thread_.
void ExecuteCallback(const TupleType& param) {
if (!canceled_) {
// Execute the callback.
callback_->RunWithParams(param);
// Notify the provider that the request is complete. The provider will
// notify the consumer for us.
NotifyCompleted();
}
}
最后,我们来看一下callback_对象,即前面NewCallback(this, &MyClass:RequestComplete)创建的。
template <class T, typename Arg1>
typename Callback1<Arg1>::Type* NewCallback(T* object,
void (T::*method)(Arg1)) {
return new CallbackImpl<T, void (T::*)(Arg1), Tuple1<Arg1> >(object, method);
}
template <class T, typename Method, typename Params>
class CallbackImpl : public CallbackStorage<T, Method>,
public CallbackRunner<Params> {
public:
CallbackImpl(T* obj, Method meth) : CallbackStorage<T, Method>(obj, meth) {
}
virtual void RunWithParams(const Params& params) {
DispatchToMethod(this->obj_, this->meth_, params);
}
};
关于DispatchToMethod,我们已经在讲Task的时候说到了。
在线源码
http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/cancelable_request.h?revision=31932
http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/cancelable_request.cc?revision=32105