Chrome thread model (Compilation)

Source: Internet
Author: User

Multi-threaded programming is very complicated, and Chrome's multi-threaded processing has a lot to learn from. This article is the design and description of threading In the chromium project. It is translated and annotated to help you understand it.

 

Simply put, we should not start too many threads but use a dedicated public thread. The code must be clear on which thread to run, and there must be a simple way to communicate between threads.

 

Original address http://dev.chromium.org/developers/design-documents/threading

 

Chrome uses multiple threads to avoid Io delays and lead to slow UI response. Chrome avoids implementing the thread security of objects by means of mutex locks. Instead, it allows objects to access only one thread and communicates messages between threads.


Although thread encapsulation classes can be used to easily create threads, chrome encourages you to use existing threads as much as possible to avoid management troubles. Each thread has a messageloop object used to process the message of the thread. You can obtain the message loop object of the thread through the thread. message_loop () method.


Existing thread

Most of the work is done on the UI thread (that is, the main thread), and other main threads include,

Io_thread is responsible for message delivery between processes (IPC) and distribution of network resource requests.
File_thread reads and writes files.
Db_thread is responsible for accessing the local SQLite Database
Safe_browsing_thread, etc.


Inter-thread Communication


To execute code in a specified thread, you must first encapsulate the code into a task object (unit of work mode), locate the messageloop object of the target thread, and call its posttask method. After the task object is put into the internal queue, posttask returns immediately. The target thread processes the task objects in the internal queue in sequence, calls the task. Run () method, and then deletes the task objects.

For example


Class mytask: public task {
Public:
Virtual void run (){
Dosomething ();
}
};

Chromethread: posttask (chromethread: file, from_here, new mytask );

 

Encapsulating a task is actually very troublesome. In particular, we often call the method of the current object. Therefore, chrome provides the newrunnablemethod helper function, which can directly encapsulate the method of an object into a task, so that it is easy to call posttask.


To call this function, you need to provide object pointers, object method pointers, and method parameters (multiple parameters are allowed and the types are not limited ). Because the object is accessed across threads, the object must be thread-safe, and the reference count is used to ensure that the object is not deleted by another thread when accessed in one thread, wait until the execution of the task ends.

For example

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 _;
};


Internally, chrome uses tuple to save function call parameters and uses value copy. Therefore, it is recommended that you do not pass bare pointers. Intelligent pointers with reference counts, such as scoped_refptr, can be passed. For example


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 ){
...
}
};


You can even specify the destructor of an object to be executed on a specific thread, for example

Class myobject: Public refcountedthreadsafe <myobject, chromethread: deleteoniothread> {


Because a task is executed asynchronously, it is possible that the object to be accessed by the task has been deleted when the task is executed. This is a common crash mode in multi-threaded programs. Therefore, it is best to cancel all unexecuted tasks before the object is deleted. Chrome's solution is to use a factory object to create a task and use the factory object as a member variable of the called object. In this way, the factory object is first destructed and all tasks created by it are automatically canceled. To use this method, you do not need to use the reference count of the called object to control the lifecycle, which is relatively simple.

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 _;
};


The factory object can be used before to avoid the crash caused by executing the task after the object is deleted. Another similar situation is that you want to cancel the task after sending a request. Similarly, chrome uses an additional object for tracking. When the object is deleted, it automatically cancels the incomplete request.

Class myclass {
Void makerequest (){
Frontend_service-> startrequest (some_input1, some_input2, this,
Newcallback (this, & myclass: requestcomplete ));
}

Void requestcomplete (INT status ){
...
}

PRIVATE:
Cancelablerequestconsumer consumer _;
};

The object to process the request must be derived from cancelablerequestprovider. This object has a method to cancel a request that has not been executed, and ensure that the caller can also get the opportunity to execute related cleanup work. If you do not need to wait for the request results, you do not need to use this method. Instead, you can directly use task/runnablemethod.

The following example shows that when a foreground module receives a request, it directly transfers the request to the background module.

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 background module returns the processing result in this way.

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 ));
}
};

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.