線程(http://www.chromium.org/developers/design-documents/threading )web
a) 概述sql
Chromium是一個超級多線程的產品,咱們嘗試讓UI的反應儘量的快,這樣就意味着不要用任何的I/O操做或者長操做來阻塞UI 線程,咱們的方法是在線程之間使用消息傳遞,咱們不鼓勵使用阻塞和線程安全的對象,取而代之的是,對象都只存在一個線程中,咱們在線程間傳遞消息通信,爲大部分的跨線程請求使用callback接口返回結果。chrome
Thread 對象定義在 base/thread.h 中,通常來講,你均可以使用這裏已經有的線程,而非本身去重寫一個新的。咱們已經有太多的線程,以至咱們很難去跟蹤。每條線程都有一個 MessageLoop (base/message_loop.h)來處理這個線程的消息,你能夠獲取每一個線程的MessageLoop,用這個函數就行 Thread.message_loop() 。數據庫
b) 已有的線程windows
大部分線程都被 BrowserProcess 對象管理,BrowserProcess對象就像是「browser」進程的服務管理者同樣,通常全部的事情都發生在UI線程上(也就是程序開始的主線程),咱們將某些類型的處理壓進這些線程裏,如下這些線程均可以獲取接收這些處理:瀏覽器
io_thread:分發線程,處理 browser 進程和 子進程之間的通訊,也是全部資源請求(webpage loads)調配的地方。緩存
file_thread:一個處理文件的線程, 當你想作一些阻塞的文件系統的操做時,派遣到這個線程。安全
db_thread:數據庫操做的線程,例如,cookie service 會在這個線程裏作一些 sqlite 的操做。注意,history 不會使用這個線程。cookie
safe_browser_thread數據結構
有幾個組件擁有它們本身的線程:
history:history service 有本身的線程,其實它能夠合併到 db_thread 中,不過,咱們須要肯定事情發生的精確順序,例如,cookies load會先於 history load,由於cookies須要被先讀,並且 history 的初始化比較長而且是阻塞的。
Proxy service :net/http/http_proxy_service.cc.
Automation proxy :這個線程用來和 UI test 程序通訊,驅動應用。
c) 讓瀏覽器保持良好的響應性
在概述中能夠看到,咱們避免在UI線程中作任何的阻塞IO操做,以便讓UI保持良好的響應性,另外一個隱藏的意思是,咱們也須要避免在IO 線程裏作阻塞的IO 操做,緣由是,若是咱們在IO 線程中作耗時操做,例如磁盤讀寫,那麼IPC 消息就不會被及時處理,會影響到用戶和頁面的交互,咱們能夠用 異步IO或者完成端口 來作這個事情。
另一個須要注意的是,不要線程間彼此阻塞,鎖只能用於多線程間共享的數據結構,若是一個線程執行比較耗時的操做或者磁盤操做的時候,就應該放開鎖,只有獲得結果的時候,才使用鎖來交換新數據。這裏是一個例子:PluginList::LoadPlugins (src/webkit/glue/plugins/plugin_list.cc) ,若是你非要用鎖,這裏是一些比較好的實踐幫助你避免一些陷阱(http://www.chromium.org/developers/lock-and-condition-variable )。
爲了寫 non-blocking 的代碼,chrome的不少 API 都是異步的,這也意味着,這些代碼多是運行在某個線程中,而後經過某些委託接口返回;或者他們會在請求操做完成時候會觸發一個base::Callback<> 對象。執行操做會在下面的 PostTask 章節中說到。
d) 線程中的技術點
i. base::Callback<>, Async APIs, and Currying
base::Callback<>(callback.h ) 是一個模版類,有一個Run函數,這個函數是函數指針的泛化,這個Callback對象是被 base::Bind 函數建立的,異步API常常會採用 base::Callback<> 做爲一種手段來異步返回操做的結果,下面是一個例子:
void ReadToString(const std::string& filename, const base::Callback<void(const std::string&)>& on_read);
void DisplayString(const std::string& result) {
LOG(INFO) << result;
}
void SomeFunc(const std::string& file) {
ReadToString(file, base::Bind(&DisplayString));
};
在這個例子裏,base::Bind 將 DisplayString 這個函數變成了 base::Callback<void(const std::string& result)> 對象,base::Callback<> 對象的類型決定於參數。爲何不直接傳函數指針呢?這是由於 base::Bind 容許調用者去適配函數接口,而且經過 Curring(http://en.wikipedia.org/wiki/Currying ) 附上一些額外的內容,例如,咱們已經有一個工具函數 DisplayStringWithPrefix 能夠用參數做爲前綴,咱們使用 base::Bind 來適配這個接口以下:
void DisplayStringWithPrefix(const std::string& prefix, const std::string& result) {
LOG(INFO) << prefix << result;
}
void AnotherFunc(const std::string& file) {
ReadToString(file, base::Bind(&DisplayStringWithPrefix, "MyPrefix: "));
};
這能夠用來代替建立一個適配函數,擁有一個前綴成員變量的小類。還要注意的是「MyPrefix:」參數其實是一個const char *,而DisplayStringWithPrefix實際上想要一個const std::string。像正常功能調度,base::Bind,將強制轉換參數的類型,若是可能的話。請參閱「 base:: bind()如何處理參數」 會講更多的細節,關於參數存儲,複製和特殊處理的參考。
ii. PostTask
分配給另外線程最底層的函數是使用 MessageLoop.PostTask 和 MessageLoop.PostDelayTask( base/message_loop.h)。
PostTask分配了一個task在指定的線程中運行,task 被定義爲 base::Closure,
base::Closure 是一個 typedef :
Typedef base::Closure base::Callback<void(void)>
PostDelayedTask 分配一個task給指定線程,這個task會延遲一段時間再執行,一個task被typedef 爲 base::Closure,這個定義實際是一個Callback對象,包含了一個Run函數,這個對象被 base::Bind()函數建立,處理一個task,message_loop最終會調用 base::Closure 的 Run 函數,而後會把task 對象的引用 drops。
PostTask 和 PostDelayTask 都有一個tracked_objects::Location 的參數,這個參數是爲了知足輕量級的調試用途(task計數、正等待的task以及已完成的task均可以經過Url about:objects被監測到,固然咱們須要debug版本)。通常來講,FROM_HERE 宏是這個參數的適當的值。
注意,新task在message_loop裏運行,咱們指定的任何delay值,都會受制於操做系統的timer 精度,這就意味這,在windows下,很是小的timeout,例如10ms如下,將有可能沒法實現(delay會更長)。使用0做爲PostDelayedTask 的參數,就等價於直接調用 PostTask。
PostTask也用來在當前線程中作一些事情,有時候在message_loop的當前處理返回以後。這樣一個在當前線程的延續操做,將能夠用來保證這個線程中的其餘關鍵任務不會被「餓死」。
下面的例子是爲一個函數建立一個task,並post它到別的線程(這個例子是 post到 file thread):
void WriteToFile(const std::string& filename, const std::string& data);
BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
base::Bind(&WriteToFile, "foo.txt", "hello world!"));
你應該老是使用 BrowserThread 在線程間投遞task,不要緩存MessageLoop的 指針,由於這會形成一些bug,例如這些指針已經被刪除了,你卻還在使用他們。更多的信息能夠參考這裏:http://www.chromium.org/developers/design-documents/threading/suble-threading-bugs-and-patterns-to-avoid-them
iii. base::Bind() and class method
base::Bind API 也支持調用類方法,語法很是相似 base::Bind 通常的函數,除了第一個參數是類對象,默認狀況下,PostTask 使用的對象應該是線程安全、使用引用計數的對象,引用計數保證對象在另外一個線程中調用將繼續存在,直到task完成。
class MyObject : public base::RefCountedThreadSafe<MyObject> {
public:
void DoSomething(const std::string16& name) {
thread_->message_loop()->PostTask(
FROM_HERE, base::Bind(&MyObject::DoSomethingOnAnotherThread, this, name));
}
void DoSomethingOnAnotherThread(const std::string16& 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_;
};
若是你有一個外部的同步結構,而且你能夠徹底確定這個對象會在task等待執行的過程當中一直存在,那麼你能夠在調用 base::Bind() 的時候使用 base::Unretained() 封裝這個對象指針,這樣能夠關閉引用計數。這個也容許用在不用引用計數的類中,不過當你這樣作的時候,要很是當心。
iv. base::Bind() 如何處理參數
參數傳遞給 base::Bind() 的時候會拷貝到一個內部的 InvokerStorage 結構對象( base/bind_internal.h)。當這個函數執行完的時候,能看到參數的拷貝。若是你的目標函數或者方法使用一個 const 的引用,這就很是重要了。若是你須要一個原始參數的引用,你可使用 base::ConstRef() 來包裝這個參數。當心使用這個,由於若是你不能保證這個引用直到task執行完成以後都存在,那就會很危險了。尤爲是一個變量在堆上建立的時候,使用 base::ConstRef() 是很不安全的,除非你能保證這個堆一直有效,直到完成這個異步task。
有時候,你會想傳一個引用計數的對象做爲參數(記得使用 RefCountedThreadSafe 和不純的RefCounted 做爲基類),保證對象在整個請求的過程當中都存在,base:Bind 生成的 Closure 應該保持一個引用,這能夠在傳遞 scoped_refptr 做爲參數類型或者使用 make_scoped_refptr 包裝原始指針的時候來作這個事情。
class SomeParamObject : public base::RefCountedThreadSafe<SomeParamObject> {
...
};
class MyObject : public base::RefCountedThreadSafe<MyObject> {
public:
void DoSomething() {
scoped_refptr<SomeParamObject> param(new SomeParamObject);
thread_->message_loop()->PostTask(FROM_HERE
base::Bind(&MyObject::DoSomethingOnAnotherThread, this, param));
}
void DoSomething2() {
SomeParamObject* param = new SomeParamObject;
thread_->message_loop()->PostTask(FROM_HERE
base::Bind(&MyObject::DoSomethingOnAnotherThread, this,
make_scoped_refptr(param)));
}
// Note how this takes a raw pointer. The important part is that
// base::Bind() was passed a scoped_refptr; using a scoped_refptr
// here would result in an extra AddRef()/Release() pair.
void DoSomethingOnAnotherThread(SomeParamObject* param) {
...
}
};
你若是想直接傳對象而不是傳它的引用,那你能夠用 base::Unretained(). 再次提醒,使用這個須要很是的當心。
若是你的對象有一個實際的析構函數,須要運行在某個線程裏,你可使用下面的這個trait,這個是須要的,由於一個時間前後的問題會致使一個task在代碼投遞以前完成執行,這樣不會破壞堆棧。
class MyObject : public base::RefCountedThreadSafe<MyObject, BrowserThread::DeleteOnIOThread> {
...};
v. base::WeakPtr 和 Cancellation
咱們有時候會在咱們的對象「以後」幹一些事情,好比你用 PostDelayTask,當你的task被調用的時候,卻發現你的對象已經被刪除了,這種事情致使大量的Crash,尤爲是在程序退出的時候。
在這種狀況下,你能夠用 base::WeakPtr 和 base::WeakPtrFactory ( base/memory/weak_ptr.h ) 來肯定任何的調用都不能發生在對象的生命週期以外,這裏沒有使用引用計數。base::Bind 函數機制會特別清楚 base::WeakPtr 對象,若是這個對象無效的時候,base::Bind 會中止task的執行。
base::WeakPtrFactory 用來生成多個 base::WeakPtr 實例,當 Factory 對象銷燬,則全部的 WeakPtr 會將他們內部的 "invalidated " 標誌置爲true,這樣會致使綁定在他們身上的task不能分發。(task能夠綁定在 WeakPtr 上面),將 factory 做爲分發對象的成員變量,你能夠擁有這個「自動消除」的屬性。
class MyObject {
public:
MyObject() : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
}
void DoSomething() {
const int kDelayMS = 100;
MessageLoop::current()->PostDelayedTask(FROM_HERE,
base::Bind(&MyObject::DoSomethingLater, weak_factory_.GetWeakPtr()),
kDelayMS);
}
void DoSomethingLater() {
...
}
private:
base::WeakPtrFactory<MyObject> weak_factory_;
};
vi. Cancelable request (可取消請求)
可取消請求讓它能輕鬆的向別的線程提出請求,而後那個線程異步給你返回一些數據。就像可撤銷的店鋪系統同樣,咱們使用對象,這些對象能夠跟蹤它的源對象是否存在,若是調用對象已經被刪除了,那這個請求也會取消,以免無效的回調。
像可撤銷的店鋪系統同樣,一個可取消請求的用戶擁有一個對象(這裏稱之爲 "Consumer"),它跟蹤對象是否還存在,而且會自動刪除任何未完成的請求。
class MyClass {
void MakeRequest() {
frontend_service->StartRequest(some_input1, some_input2, this,
// Use base::Unretained(this) if this may cause a refcount cycle.
base::Bind(&MyClass:RequestComplete, this));
}
void RequestComplete(int status) {
...
}
private:
CancelableRequestConsumer consumer_;
};
注意這個MyClass::RequestComplete ,是和 base::Unretained(this) 綁定在一塊兒了。
Consumer 容許你附加額外的數據在一個請求上,使用 CancelableRequestConsumer 將容許你附加任意的數據,這些數據當你調用請求的時候,會被 provider service 返回。當請求取消,數據將會自動銷燬。
一個處理請求的服務繼承自 CancelableRequestProvider, 這個對象提供了能夠取消輕量級請求的函數,而且將會和 consumers 一塊兒保證在取消的時候,全部東西都被正確的清理。這個 frontend service 只是跟蹤而且發送到另一個線程上的 backend service,它看起來就像這樣:
class FrontendService : public CancelableRequestProvider {
typedef base::Callback<void(int)> RequestCallbackType;
Handle StartRequest(int some_input1, int some_input2,
CallbackConsumer* consumer,
const RequestCallbackType& callback) {
scoped_refptr< CancelableRequest<FrontendService::RequestCallbackType> >
request(new CancelableRequest(callback));
AddRequest(request, consumer);
// Send the parameters and the request to the backend thread.
backend_thread_->PostTask(FROM_HERE,
base::Bind(&BackendService::DoRequest, backend_, request,
some_input1, some_input2), 0);
// The handle will have been set by AddRequest.
return request->handle();
}
};
backend service 在另外一個線程中運行,它處理並把結果轉發回原始的調用者,以下:
class BackendService : public base::RefCountedThreadSafe<BackendService> {
void DoRequest(
scoped_refptr< CancelableRequest<FrontendService::RequestCallbackType> >
request,
int some_input1, int some_input2) {
if (request->canceled())
return;
... do your processing ...
// Execute ForwardResult() like you would do Run() on the base::Callback<>.
request->ForwardResult(return_value);
}
};