在前面介绍的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
分享到:
相关推荐
chrome网页性能分析 教程.zip 统计图 颜色 垃圾回收
主要是对chrome UI的源代码进行分析和相关讲解
chrome浏览器开源技术分析文档,包括webkit v8 多线程模式,多进程,沙箱
chrome 破解分析chrome 破解分析
下面就可以先尝试修改一个chrome的关于对话框,上一次看到它是英语的,那么我就来把它改成中文的吧,这样有目标了。从chrome的工程里可以看到它是支持多种语言的,在Windows平台上支持多语言的标准做法,就是写多个...
Chrome谷歌浏览器内核(Rendering Engine)源码分析
cssdig-chrome, 用于分析CSS的Chrome 扩展 CSS挖掘 Chrome安装CSS挖掘为 ChromeCSS挖掘主页这里 Chrome 扩展查找网页上运行的样式表和样式块,并将声明组合在一起,便于检查。 例如你可以看到使用了多少种颜色,以及...
chrome好用的插件,掘金里相当于好多比较新的资讯和话题,画面也挺漂亮的,适合程序员阅读的插件。 至于插件的使用这里我就不多说了
chrome chrome chrome chrome
chrome插件XSwitch chrome插件XSwitch
chrome chrome
Axure RP Chrome 0.6.4 原型工具Chrome扩展插件,支持最新版本Chrome浏览器,安装方法如下(亲测可用): 1、打开谷歌浏览器,在浏览器中地址栏中输入chrome://extensions/,或者点击浏览器的右上角选择更多工具,...
在国内使用Chrome浏览器的小伙伴,一直都比较愁。Chrome数据同步的问题。原因大家都懂。 有什么好办法来同步Chrome的数据呢?这里给大家推荐一个Chrome扩展。 官方网站 ...
autoit控制chrome的扩展库,正式拜托麻烦的IE
谷歌浏览器网页元素分析工具+Chrome_SPY+元素捕获+PRA
前端js加密解决方案及步骤教学,大神整理前端js逆向分析实例
Google Chrome Backup 是一款完整备份Chrome设置(包括历史和扩展), Chrome 历史记录、书签、扩展等等信息的工具,支持 Chromium 及 Portable 版本。 如果使用的是安装版的 Chrome,那么运行 Google Chrome Backup...
(1)vue 2.x 调试工具:vue_devtools_chrome_5.3.4.crx Chrome浏览器Vue调试插件; (2)直接安装使用无需自己编译; (3)安装方法:打开Chrome浏览器,输入“chrome://extensions/”打开扩展程序,启用开发者模式...
Axure RP Extension for Chrome是原型设计工具Axure RP的Chrome浏览器插件。因为在线安装需要访问Google Chrome在线商店,访问不了。所以提供一个离线版本进行安装。 安装方法: 1、打开Chrome浏览器,找到“工具 ->...
谷歌浏览器 Chrome 安装包 ChromeSetup.exe