在前面介紹的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=31932http://src.chromium.org/viewvc/chrome/trunk/src/chrome/browser/cancelable_request.cc?revision=32105