注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

写着玩

Bob

 
 
 

日志

 
 
 
 

Threading  

2010-01-10 21:13:17|  分类: Chrome |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

Overview

Chromium is a very multithreaded product. We try to keep the UI as responsive as possible, and this means not blocking the UI thread with any blocking I/O or other expensive operations. Our approach is to use message passing as the way of communicating between threads. We discourage locking and threadsafe objects. Instead, objects live on only one thread, we pass messages between threads for communication, and we use callback interfaces (implemented by message passing) for most cross-thread requests.

The de style="color: rgb(0, 96, 0); "<Threadde< object is defined in de style="color: rgb(0, 96, 0); "<base/thread.hde<. In general you should probably use one of the existing threads described below rather than make new ones. We already have a lot of threads that are difficult to keep track of. Each thread has a de style="color: rgb(0, 96, 0); "<MessageLoopde< (see de style="color: rgb(0, 96, 0); "<base/message_loop.hde<) that processes messages for that thread. You can get the message loop for a thread using the de style="color: rgb(0, 96, 0); "<Thread.message_loop()de< function.

Existing threads

Most threads are managed by the de style="color: rgb(0, 96, 0); "<BrowserProcessde< object, which acts as the service manager for the main "browser" process. By default, everything happens on the UI thread (the main thread where the application starts up). We have pushed certain classes of processing into these other threads. It has getters for the following threads:

  • io_thread: This thread is somewhat mis-named. It is the dispatcher thread that handles communication between the browser process and all the sub-processes. It is also where all resource requests (web page loads) are dispatched from (see Multi-process Architecture).
  • file_thread: A general process thread for file operations. When you want to do blocking filesystem operations (for example, requesting an icon for a file type, or writing downloaded files to disk), dispatch to this thread.
  • db_thread: A thread for database operations. For example, the cookie service does sqlite operations on this thread. Note that the two major databases (history and web data) don't use this thread yet.
  • safe_browsing_thread

Several components have their own threads:

  • History: The history service object has its own thread. This might be merged with the db_thread above. However, we need to be sure that things happen in the correct order -- for example, that cookies are loaded before history since cookies are needed for the first load, and history initialization is long and will block it.
  • Web data: The web data service is similar to history, but it stores random data for web browsing such as saved passwords, keywords, and installed applications.
  • Proxy service: See de style="color: rgb(0, 96, 0); "<net/http/http_proxy_service.cc.de<
  • Automation proxy: This thread is used to communicate with the UI test program driving the app.

Getting stuff to other threads

PostTask

The lowest level of dispatching to another thread is to use the de style="color: rgb(0, 96, 0); "<MessageLoop.PostTaskde< and de style="color: rgb(0, 96, 0); "<MessageLoop.PostDelayedTaskde< (see de style="color: rgb(0, 96, 0); "<base/message_loop.hde<).PostTask schedules a task to be run on a particular thread. PostDelayedTask schedules a task to be run after a delay on a particular thread. A task is a simple virtual interface that defines one function: de style="color: rgb(0, 96, 0); "<void Run()de<. To process a task, the message loop eventually calls the Task's Run function, and then deletes the task. Both PostTask and PostDelayedTask take a tracked_objects::Location parameter, which is used for lightweight debugging purposes (counts and primitive profiling of pending and completed tasks can be monitored in a debug build via the url about:objects). Generally the macro valueFROM_HERE is the appropriate value to use in this parameter.

Note that new tasks go on the message loop's queue, and any delay that is specified is subject to the operating systems' timer resolutions. This means that under Windows, very small timeouts (under 10ms) will likely not be honored (and will be longer). Using a timeout of 0 in PostDelayedTask is equivalent to calling PostTask, and adds no delay beyond queuing delay. PostTask is also used to do something on the current thread "sometime after the current processing returns to the message loop." Such a continuation on the current thread can be used to assure that other time critical tasks are not starved on the current thread.

The following is an example of a class declaring a specialized task that can be posted, along with an example of of how it could be posted to (in this example) the file thread:

class MyTask : public Task {
public:
virtual void Run() {
DoSomething();
}
};

ChromeThread::PostTask(ChromeThread::FILE, FROM_HERE, new MyTask);
You should always use ChromeThread to post tasks between threads.  Never cache MessageLoop pointers as it can cause bugs such as the pointers being deleted while you're still holding on to them.  More information can be found here.

Runnable methods

It is annoying to have to create small task objects for everything you need to do on another thread. To make it easier, we have a helper functions calledde style="color: rgb(0, 96, 0); "<NewRunnableMethodde< (in de style="color: rgb(0, 96, 0); "<base/task.hde<) to assist you. It will automatically construct a templatized Task to call functions on objects.

These functions take an object, a pointer to a function on that object, and optional arguments to the function (overrides allow different number of arguments). The object that de style="color: rgb(0, 96, 0); "<PostTaskde< uses must be be a thread-safe reference-counted object. Reference counting ensures that the object invoked on another thread will stay alive until the task completes.

class MyObject : public RefCountedThreadSafe<MyObject> {
public:
void DoSomething(const std;:wstring& name) {
thread_->message_loop()->PostTask(FROM_HERE
NewRunnableMethod(this, &MyObject::DoSomethingOnAnotherThread, name));
}

void DoSomethingOnAnotherThread(const std::wstring& name) {
...
}
 private:
// Always good form to make the destructor private so that only RefCountedThreadSafe can access it.
// This avoids bugs with double deletes.
friend class base::RefCountedThreadSafe<MyObject>;

~MyObject();
  Thread* thread_;
};

The arguments in the runnable method will be inferred from the types of the arguments of the destination function. A de style="color: rgb(0, 96, 0); "<Tuplede< (defined in de style="color: rgb(0, 96, 0); "<base/tuple.hde<) is used to store these parameters and dispatch to the final function. Object arguments (integers, etc.) will be copied. Pointer values, but not the object they point to, will be copied, so you probably don't want to pass pointers.

There is a special de style="color: rgb(0, 96, 0); "<TupleTraitsde< (in de style="color: rgb(0, 96, 0); "<base/tuple.hde<) specialization defined for reference arguments so that they will be copied In the example above, the de style="color: rgb(0, 96, 0); "<namede<parameter in de style="color: rgb(0, 96, 0); "<DoSomethingOnAnotherThreadde< will actually refer to a copy of the original string stored in the tuple (inside the object generated byde style="color: rgb(0, 96, 0); "<NewRunnableMethodde<). If you have an object that needs special copying semantics, you can define your own de style="color: rgb(0, 96, 0); "<TupleTraitsde< specialization for it.

Sometimes, you will want to pass reference-counted objects as parameters (be sure to use de style="color: rgb(0, 96, 0); "<RefCountedThreadSafede< and not plain de style="color: rgb(0, 96, 0); "<RefCountedde< as the base class for these objects). To ensure that the object lives throughout the entire request, the de style="color: rgb(0, 96, 0); "<Taskde< generated by de style="color: rgb(0, 96, 0); "<NewRunnableMethodde< must keep a reference to it. This can be done by passing de style="color: rgb(0, 96, 0); "<scoped_refptrde< as the parameter type:

class SomeParamObject : public RefCountedThreadSafe<SomeParamObject> {
...
};

class MyObject : public RefCountedThreadSafe<MyObject> {
public:
void DoSomething() {
scoped_refptr<SomeParamObject> param(new SomeParamObject);
thread_->message_loop()->PostTask(FROM_HERE
NewRunnableMethod(this, &MyObject::DoSomethingOnAnotherThread, param));
}

void DoSomethingOnAnotherThread(scoped_refptr<SomeParamObject> param) {
...
}
};
If your object has a non-trivial destructor that needs to run on a specific thread, you can use the following trait.  This is needed since timing races could lead to a task completing execution before the code that posted it has unwound the stack.
class MyObject : public RefCountedThreadSafe<MyObject, ChromeThread::DeleteOnIOThread> {

Scoped factories

Sometimes you will want to do something "later" on your object. You use PostDelayedTask with a timer value and issue it on the current thread. But what happens if your source object's lifetime is managed by another component, and it gets deleted while the task is waiting to fire? This has been the cause of a number of crashes, especially on shutdown.

In these cases, you can use a de style="color: rgb(0, 96, 0); "<RevokableStorede< (in de style="color: rgb(0, 96, 0); "<base/revokable_store.hde<) to ensure that any invokes can not outlive the object they are being invoked on, without using reference counting. A "factory" object will generate special tasks that know about the factory object. When the factory is destroyed, all the tasks will have their internal "revoked" flag set, which will cause them to not dispatch to the original object. By putting the factory as a member of the object being dispatched to, you can get automatic canceling.

class MyObject {
public:
MyObject() : factory_(this) {
}

void DoSomething() {
const int kDelayMS = 100;
MessageLoop::current()->PostDelayedTask(FROM_HERE,
factory_.NewRunnableMethod(&MyObject::DoSomethingLater),
kDelayMS);
}

void DoSomethingLater() {
...
}

private:
ScopedRunnableMethodFactory<MyObject> factory_;
};

Cancelable request

A cancelable request makes it easier to make requests to another thread with that thread returning some data to you asynchronously. Like the revokable store system, it uses objects that track whether the originating object is alive. When the calling object is deleted, the request will be canceled to prevent invalid callbacks.

Like the revokable store system, a user of a cancelable request has an object (here, called a "Consumer") that tracks whether it is alive and will auto-cancel any outstanding requests on deleting.

class MyClass {
void MakeRequest() {
frontend_service->StartRequest(some_input1, some_input2, this,
NewCallback(this, &MyClass:RequestComplete));
}

void RequestComplete(int status) {
...
}

private:
CancelableRequestConsumer consumer_;
};

The consumer also allows you to associate extra data with a request. Use de style="color: rgb(0, 96, 0); "<CancelableRequestConsumerTde< which will allow you to associate arbitrary data with the handle returned by the provider service when you invoke the request. The data will be automatically destroyed when the request is canceled.

A service handling requests inherits from de style="color: rgb(0, 96, 0); "<CancelableRequestProviderde<. This object provides methods for canceling in-flight requests, and will work with the consumers to make sure everything is cleaned up properly on cancel. This frontend service just tracks the request and sends it to a backend service on another thread for actual processing. It would look like this:

class FrontendService : public CancelableRequestProvider {
typedef Callback1::Type RequestCallbackType;

Handle StartRequest(int some_input1, int some_input2,
CallbackConsumer* consumer,
RequestCallbackType* callback) {
scoped_refptr > request(
new CancelableRequest(callback));
AddRequest(request, consumer);

// Send the parameters and the request to the backend thread.
backend_thread_->PostTask(FROM_HERE,
NewRunnableMethod(backend_, & BackendService::DoRequest, request,
some_input1, some_input2), 0);

// The handle will have been set by AddRequest.
return request->handle();
}
};

The backend service runs on another thread. It does processing and forwards the result back to the original caller. It would look like this:

class BackendService : public RefCountedThreadSafe<BackendService> {
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(FrontendService::RequestCallbackType::TupleType(
return_value));
}
};
  评论这张
 
阅读(488)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017