`
optman
  • 浏览: 44633 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Chrome CancelableRequest类分析

阅读更多

在前面介绍的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网页性能分析 教程.zip 统计图 颜色 垃圾回收

    google chrome代码分析

    关于对话框的显示逻辑位于`about_chrome_view.cc`文件中,该文件定义了一个名为`AboutChromeView`的类,这个类负责关于对话框的显示和交互。通过这个类,我们可以追踪到对话框何时被创建、如何加载资源字符串以及...

    Chrome谷歌浏览器源码分析

    Chrome谷歌浏览器源码分析 Chrome浏览器是全球最受欢迎的网络浏览器之一,其强大的性能、安全性和易用性得益于其先进的设计理念和高效的技术实现。本篇将深入探讨Chrome的核心组件——渲染引擎(Rendering Engine)...

    Chrome UI 框架分析

    主要是对chrome UI的源代码进行分析和相关讲解

    基于Chrome扩展的网页词频分析与词云图生成设计源码

    该项目是一个基于Chrome扩展的网页词频分析与词云图生成工具,主要使用JavaScript语言开发...这个工具是对chrome-extensions-samples-main项目的改进,采用MIT许可开源,旨在帮助用户分析网站内容的词频并生成词云图。

    Chrome_autoit操作chrome_autoit3chrome_autoit_autoitchrome_chrome_源

    在标题和描述中提到的"Chrome_autoit操作chrome_autoit3chrome_autoit_autoitchrome_chrome_源",主要涉及到使用AutoIt来控制Google Chrome浏览器。 首先,让我们深入了解一下AutoIt。AutoIt是一个免费的工具,它...

    谷歌浏览器网页元素分析工具+Chrome_SPY+元素捕获+PRA

    标题中的“谷歌浏览器网页元素分析工具+Chrome_SPY+元素捕获+PRA”涉及到的是在Web开发和调试过程中常用的一些技术与工具。下面将详细解释这些概念。 1. **谷歌浏览器**(Google Chrome):这是一款由谷歌公司开发...

    chrome 破解分析

    chrome 破解分析chrome 破解分析

    cssdig-chrome, 用于分析CSS的Chrome 扩展.zip

    cssdig-chrome, 用于分析CSS的Chrome 扩展 CSS挖掘 Chrome安装CSS挖掘为 ChromeCSS挖掘主页这里 Chrome 扩展查找网页上运行的样式表和样式块,并将声明组合在一起,便于检查。 例如你可以看到使用了多少种颜色,以及...

    chrome99.0.4844.51以及chrome driver

    Chrome浏览器是Google开发的一款流行的网页浏览器,以其稳定性和高性能赢得了全球用户的喜爱。版本号99.0.4844.51代表了这个特定版本的Chrome,它可能包含了安全更新、性能优化以及对Web标准的支持增强。"Chrome ...

    chrome的es插件

    标题中的“chrome的es插件”指的是在Google Chrome浏览器中使用的Elasticsearch(简称ES)插件。Elasticsearch是一种流行的、开源的全文搜索引擎,常用于大数据分析和实时搜索。这款插件允许用户通过浏览器界面与...

    chrome无法安装时的万能解决办法

    * Chrome 安装失败的原因分析 * Chrome 安装包的组成结构 * 使用压缩程序解压安装包的技巧 结语 本篇文章提供了详细的解决方案,帮助您解决 Chrome 无法安装的问题。同时,我们也提供了一些相关的知识点和概念,...

    Chrome UI 分析

    Chrome UI(用户界面)分析涉及多个层面,包括框架结构、排版绘制机制、消息分发、以及各种UI组件的设计与实现。以下是对Chrome UI分析中的知识点的详细阐述。 首先,Chrome UI框架的总体结构涉及通用代码的存放,...

    Chrome_SPY 谷歌浏览器网页元素分析工具

    Chrome_SPY 谷歌浏览器网页元素分析工具 Chrome_激活标签 (当前标签) 网页.跳转 (“www.baidu.com”) 程序__等待 (1000) 网页.取元素坐标 (“input”, “id=kw丨name=wd”, 0, 元素坐标) 提示 (“元素坐标 ...

    chrome 59 chrome 59

    10. **开发者工具**:Chrome DevTools也得到了更新,加入了新的调试和分析功能,如性能面板的改进,以及对 Progressive Web App (PWA) 更好的支持。 文件"59.0.3071.104_chrome_installer.exe"是Chrome 59的安装...

    Axure RP Chrome 0.6.4 Axure Chrome扩展插件

    Axure RP Chrome 0.6.4 原型工具Chrome扩展插件,支持最新版本Chrome浏览器,安装方法如下(亲测可用): 1、打开谷歌浏览器,在浏览器中地址栏中输入chrome://extensions/,或者点击浏览器的右上角选择更多工具,...

    ActiveXforChrome.zip

    ActiveX for Chrome是一款专为谷歌浏览器(Chrome)设计的ActiveX控件,允许用户在Chrome浏览器上运行需要ActiveX技术的网页应用。ActiveX是一种由微软开发的技术,主要用于Internet Explorer,它提供了创建和使用...

    windows chrome96.0-99.0离线包

    chrome96.0-99.0离线包,chrome96.0-99.0离线包chrome96.0-99.0离线包chrome96.0-99.0离线包chrome96.0-99.0离线包chrome96.0-99.0离线包chrome96.0-99.0离线包chrome96.0-99.0离线包chrome96.0-99.0离线包chrome...

    chrome v44 浏览器

    7. **开发者工具**:Chrome的开发者工具是前端开发的重要辅助,V44可能引入了新的调试功能、性能分析工具,以及对移动设备模拟的改进。 8. **性能监控**:可能加入了更精确的性能监控工具,帮助开发者识别和解决...

Global site tag (gtag.js) - Google Analytics