threading
概覽:
Chromium是一個(gè)有著諸多線程的產(chǎn)品。我們盡量保證UI的快速響應(yīng),這就意味著不能有任何的堵塞的I/O或者耗時(shí)操作來堵塞UI線程。我們把在線程間傳遞消息作為線程間通信的方法。我們摒棄了鎖和線程安全對(duì)象,取而代之的是一個(gè)對(duì)象只生存在一個(gè)線程中,我們通過在線程間傳遞消息來通訊,并且我們使用回掉接口(由發(fā)送消息者實(shí)現(xiàn))來完成線程間的請(qǐng)求。
Thread在base/thread.h中定義,我們已經(jīng)有很多線程以至難以跟蹤了,大體上你可以使用一個(gè)下面列表中已有的線程而不是新建一個(gè)線程。每個(gè)線程都有一個(gè)MessageLoop(參考:base/message_loop.h)為線程處理消息。你可以通過Thread.message_loop()來獲取消息循環(huán)。
大部分的線程都是由BrowserProcess對(duì)象來管理的,BrowserProcess在“browser”主進(jìn)程中扮演者服務(wù)管理者的角色。一般情況下,所有事件都是發(fā)生在UI線程(應(yīng)用程序啟動(dòng)的主線程)。我么把具體的某個(gè)處理類內(nèi)置到了其他線程,它(BrowserProcess)具有以下線程的getter方法??:
- io_thread:該線程并不名副其實(shí)。它其實(shí)是個(gè)派發(fā)線程,它負(fù)責(zé)瀏覽器主線程和其他子線程的通信。它也是所有資源請(qǐng)求(網(wǎng)頁加載)的來源。
- file_thread:文件操作的線程。當(dāng)你要執(zhí)行一個(gè)阻塞的文件系統(tǒng)操作(例如,加載一個(gè)文件中的icon,或者寫入下載的文件),發(fā)送消息到該線程。
- db_thread:數(shù)據(jù)庫操作的線程。例如,cookie服務(wù)會(huì)在該線程執(zhí)行數(shù)據(jù)庫操作。注意:歷史記錄數(shù)據(jù)庫不在該線程操作。
- safe_browsing_thread
幾個(gè)組建由他們自己的線程:
- History:歷史記錄對(duì)象有自己的線程。看起來可以和上面的數(shù)據(jù)庫線程合并。然而,我們需要保證某些事件按照正確的順序執(zhí)行--例如:cookies由于第一次啟動(dòng)的時(shí)候需要比歷史先加載,并且加載歷史十分耗時(shí)會(huì)阻塞住。
- ProxyServices:可以查閱net/http/http_proxy_service.cc。
- Automation proxy:測試程序通過該線程與瀏覽器通信,控制瀏覽器行為。
保持瀏覽器及時(shí)響應(yīng)
正如在概述中提示的,我們極力的避免在UI線程做阻塞的I/O操作來保持UI的及時(shí)響應(yīng)。更加需要注意的是我們也需要盡量避免在IO線程做阻塞的I/O操作。如果我們阻塞住,意味著磁盤操作,然后IPC消息也無法得到處理。表現(xiàn)就是用戶無法與頁面交互。需要注意的是異步/重疊 IO是個(gè)不錯(cuò)的選擇。
另外需要注意的是線程之間不要互相有阻塞的操作。索只有在多個(gè)線程共享交換數(shù)據(jù)的結(jié)構(gòu)中使用。這樣,如果一個(gè)線程更新有一個(gè)很耗計(jì)算的操作或者文件訪問就不會(huì)等待解鎖。只有當(dāng)結(jié)果是可預(yù)期的時(shí)候才可以用鎖來完成數(shù)據(jù)交換。一個(gè)例子是Plugin::LoadPlugins(src/webkit/glue/plugins/plugin_list.cc),如果你必須使用鎖,這里有很好的練習(xí)以防調(diào)入陷阱。
為了寫出非阻塞的代碼,很多API在chromium都是異步的。通常情況下,這就意味著要么需要在特定的線程執(zhí)行然后通過定制的委托接口吧結(jié)果傳回來,要么在請(qǐng)求結(jié)束的時(shí)候調(diào)用base::CallBack<>對(duì)象。在指定線程執(zhí)行任務(wù)在PostTask的部分有具體描述。
Getting stuff to other threads
base::CallBack<>,Async APIs,and Currying
base::CallBack<>(參見docs in callback.h)是一個(gè)模版類,內(nèi)部有個(gè)Run()方法。他是一個(gè)全局的函數(shù)指針,在調(diào)用base::Bind的時(shí)候創(chuàng)建。異步Apis經(jīng)常會(huì)把base::CallBack<>作為異步返回操作結(jié)果的方法。下面是一個(gè)假想的異步讀取文件的API:
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為參數(shù),并且轉(zhuǎn)化為一個(gè)base::Callback<void(const std::string& result)>.類型base::Callback<>是通過參數(shù)推斷的。為何不直接傳遞函數(shù)指針呢?原因就是base::Bind 允許調(diào)用者適應(yīng)函數(shù)接口通過Currying (http://en.wikipedia.org/wiki/Currying)增加一些額外的內(nèi)容。例如,如果有一個(gè)函數(shù)DisplayStringWithPrefix 有些額外的參數(shù)作為前綴,我們通過base::Bind適應(yīng)下面的接口:
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: "));
};
這樣可以用來替代通過在類中以成員變量保存前綴變量并創(chuàng)建一個(gè)適配的函數(shù)的方法。另外要注意“MyPrifx:”參數(shù)實(shí)際上是一個(gè)const char *,然而DisplayStringWithPrefix 實(shí)際上是需要const std::string& 如同通常的函數(shù)派發(fā),如果可以的話base::bind會(huì)強(qiáng)制轉(zhuǎn)換參數(shù)類型。查閱下面“How arguments are handled by base::Bind()”關(guān)于詳細(xì)的參數(shù)保存,拷貝,引用。
PostTask
派發(fā)消息到其他線程最初級(jí)的方法就是使用MessageLoop.PostTask和MessageLoop.PostDelayTask(參閱base/message_loop.h) PostTask安排任務(wù)在指定的線程上執(zhí)行。任務(wù)需要定義成base::Closure類型,它是typedef的base::Callback<void(void)>.PostDelayedTask安排一個(gè)任務(wù)在指定線程延遲執(zhí)行。一個(gè)任務(wù)就是擁有一個(gè)Run()方法的base::Closure,通過調(diào)用base::Bind()創(chuàng)建。消息循環(huán)通過調(diào)用Run方法來執(zhí)行任務(wù),然后減少對(duì)task對(duì)象的引用。PostTask和PostDelayTask都有一個(gè)參數(shù)tracked_objects::Location,它用來實(shí)現(xiàn)一個(gè)輕量級(jí)的調(diào)試(統(tǒng)計(jì)刮起和任務(wù)完成的性能,在debug版本中通過url about:objects查看),通常該參數(shù)用FROME_HERE宏來代替。
需要注意的是新的任務(wù)運(yùn)行在消息循環(huán)依賴的隊(duì)列中,所指定的任何延誤是操作系統(tǒng)的定時(shí)器決定的。這就意味著,在windows下,一個(gè)很短的延時(shí)(小于10ms)會(huì)不精確(實(shí)際上會(huì)更大一些)。使用PostDelayTask延遲0執(zhí)行和直接PostTask效果一樣。PostTask也經(jīng)常會(huì)用來給當(dāng)前線程投遞任務(wù)“有時(shí)候在當(dāng)前處理返回到消息循環(huán)的時(shí)候”這樣的一個(gè)延續(xù),可用于在當(dāng)前線程上,以確保其他時(shí)間的關(guān)鍵任務(wù)不會(huì)被餓死在此線程。
下面是一個(gè)例子,掩飾如何創(chuàng)建一個(gè)任務(wù),然后發(fā)送到另外一個(gè)線程(本例中是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來在線程間發(fā)送任務(wù)。永遠(yuǎn)不要緩存MessageLoop指針,因?yàn)闀?huì)導(dǎo)致bug,例如你還持有指針,但是它已經(jīng)被刪除了。更多內(nèi)容請(qǐng)參考這里。
base::Bind() and class method
base::Bind() API也支持類成員函數(shù)。寫法類似普通函數(shù),除了第一個(gè)參數(shù)必須是該方法所屬的對(duì)象。一般情況向,這個(gè)對(duì)象必須是 thread-safe reference-counted。引用技術(shù)確保在其他線程調(diào)用的時(shí)候該對(duì)象依然或者,知道任務(wù)完成。
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_;
};
如果你有外部同步化的結(jié)構(gòu),它可以確保任務(wù)在執(zhí)行的時(shí)候保持對(duì)象存活,那么你可以使用base::Unretained()包裝對(duì)象指針,使得調(diào)用base::Bind()的時(shí)候禁用引用計(jì)數(shù)。這個(gè)也允許使用在非引用計(jì)數(shù)的類上,請(qǐng)小心使用!
how arguments are handled by base::Bind()
參數(shù)拷貝到了內(nèi)部的結(jié)構(gòu)體(InvokeStorage)中(base/bind_internal中定義)。函數(shù)調(diào)用的時(shí)候他會(huì)拷貝參數(shù)。如果方法中有參數(shù)是常引用,這個(gè)引用會(huì)指向拷貝的參數(shù)。如果需要只想原始參數(shù)的引用,可以使用base::ConstRef()。要小心使用,例如在傳遞參數(shù)的時(shí)候不能保證存活就很危險(xiǎn)。尤其是永遠(yuǎn)不要對(duì)棧上的變量使用base::ConstRef()除非你能保證在異步調(diào)用完成前棧幀保持有效。
有時(shí)候你會(huì)想以一個(gè)引用計(jì)數(shù)的對(duì)象作為參數(shù)(保證使用的是從RefCountedThreadSafe集成的類型,而不是簡單的從RefCounted繼承的類型)。為了確保對(duì)象的生存期能夠覆蓋整個(gè)請(qǐng)求,Closure需要保有一個(gè)對(duì)象的指針。這可以通過scoped_refptr作為參數(shù)類型,或者用make_scoped_refptr()包裝一個(gè)原始的指針實(shí)現(xiàn):
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) {
...
}
};
如果你想傳遞一個(gè)對(duì)象但是不想有任何引用相關(guān)的東西,可以用base::Unretained包裝參數(shù),再次提醒,這樣意味著外部要保證這個(gè)對(duì)象的生命期,所以請(qǐng)小心使用。
如果你的對(duì)象有一個(gè)特殊的析構(gòu)函數(shù)需要運(yùn)行在指定線程,那么你可以使用下面的特性。這樣是必須的,因?yàn)闀r(shí)間競爭的關(guān)系,可能導(dǎo)致,發(fā)送任務(wù)的代碼在異常做棧展開之前,任務(wù)會(huì)完整執(zhí)行(這可能導(dǎo)致異常無法得到及時(shí)處理就因?yàn)橐呀?jīng)發(fā)送出去的任務(wù)還在執(zhí)行就崩潰了,我是這樣理解的不知道對(duì)不對(duì))。(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 base::RefCountedThreadSafe<MyObject, BrowserThread::DeleteOnIOThread> {
Callback cancellation
取消任務(wù)有兩個(gè)主要的原因(在回調(diào)的形式中):
- 你會(huì)像稍后做些什么事情,但是現(xiàn)在回調(diào)再運(yùn)行,你的對(duì)象可能已經(jīng)刪除了。
- 當(dāng)輸入變更話(例如用戶輸入),舊的任務(wù)變得不再必須,為了表現(xiàn)好些,你需要取消掉它們。
請(qǐng)看下面的幾個(gè)不同的取消任務(wù)的方法
Important notes about cancellation
使用owned parameters(其實(shí)就是非引用計(jì)數(shù)來管理對(duì)象生存期的參數(shù)).的時(shí)候取消任務(wù)是很危險(xiǎn)的,下面是個(gè)例子(例子中使用base::WeakPtr來做取消,但是問題是所有方法中都有的)
class MyClass {
public:
// Owns |p|.
void DoSomething(AnotherClass* p) {
...
}
WeakPtr<MyClass> AsWeakPtr() {
return weak_factory_.GetWeakPtr();
}
private:
base::WeakPtrFactory<MyObject> weak_factory_;
};
...
Closure cancelable_closure = Bind(object->AsWeakPtr(), p);
Callback<void(AnotherClass*)> cancelable_callback = Bind(object->AsWeakPtr());
...
void FunctionRunLater(const Closure& cancelable_closure,
const Callback<void(AnotherClass*)>& cancelable_callback) {
...
// Leak memory!
cancelable_closure.Run();
cancelable_callback.Run(p);
}
在 FunctionRunLater中, 調(diào)用 Run()的時(shí)候如果對(duì)象已經(jīng)西溝會(huì)導(dǎo)致內(nèi)存泄露使用 scoped_ptr 可以解決這個(gè)問題.class MyClass {
public:
void DoSomething(scoped_ptr<AnotherClass> p) {
...
}
...
};
base::WeakPtr and Cancellation[NOT THREAD SAFE]
你可以使用base::WeakPtr和base::WeakPtrFactory (inbase/memory/weak_ptr.h)來去報(bào)任何調(diào)用都發(fā)生在對(duì)象的生存期內(nèi),而不使用引用計(jì)數(shù)。base::Bind的機(jī)制會(huì)在base::WeakPtr無效的時(shí)候禁止任務(wù)的執(zhí)行。 base::WeakPtrFactory對(duì)象可以用來創(chuàng)建base::WeakPtr實(shí)例,這些實(shí)例知道base::WeakPtrFactory對(duì)象,當(dāng)factory被刪除的時(shí)候所有它創(chuàng)建WeakPtr會(huì)在內(nèi)部設(shè)置為失效,這樣就使得所有綁定的相關(guān)任務(wù)都不會(huì)派發(fā)執(zhí)行。把factory對(duì)象設(shè)置成要派發(fā)任務(wù)的對(duì)象的成員,就會(huì)自動(dòng)取消了。
注意:這只發(fā)生在把任務(wù)發(fā)送到本線程的情況。現(xiàn)在有另外一種解決方案可以解決發(fā)送任務(wù)到其他線程的情況,參閱下一節(jié)CancelableTaskTracker。
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_;
};
CancelableTaskTracker
雖然base::WeakPtr對(duì)于取消任務(wù)很有用,但是它并不是線程安全的,所以不能用于在其它線程運(yùn)行的任務(wù)。有時(shí)侯是些特殊的要求,例如,我們需要在用戶輸入發(fā)生改變的時(shí)候取消掉之前的數(shù)據(jù)庫查詢?nèi)蝿?wù)。這種情況下CancelableTaskTracker更合適。
你可以用CancelableTaskTracker取消單個(gè)任務(wù)通過任務(wù)ID,這個(gè)也是為啥即使在同一線程也要用它代替base::WeakPtr的原因。
CancelableTaskTracker與base::TaskRunner類似有兩個(gè)方法做相同post task的事情,只是多了些支持取消任務(wù)的支持。
class UserInputHandler : public base::RefCountedThreadSafe<UserInputHandler> {
// Runs on UI thread.
void OnUserInput(Input input) {
CancelPreviousTask();
DBResult* result = new DBResult();
task_id_ = tracker_->PostTaskAndReply(
BrowserThread::GetMessageLoopProxyForThread(BrowserThread::DB),
FROM_HERE,
base::Bind(&LookupHistoryOnDBThread, this, input, result),
base::Bind(&ShowHistoryOnUIThread, this, base::Owned(result)));
}
void CancelPreviousTask() {
tracker_->TryCancel(task_id_);
}
...
private:
CancelableTaskTracker tracker_; // Cancels all pending tasks while destruction.
CancelableTaskTracker::TaskId task_id_;
...
};
因?yàn)槿蝿?wù)在其他線程運(yùn)行,無法確保一定會(huì)成功取消。當(dāng)TryCancel()調(diào)用的時(shí)候:
- 如果任務(wù)和回復(fù)都沒有運(yùn)行,他們都將被取消
- 如果過任務(wù)已經(jīng)運(yùn)行或者已經(jīng)完成,恢復(fù)將被取消
- 如果回復(fù)已經(jīng)運(yùn)行或者已經(jīng)完成,取消就只是個(gè)空操作
與 base::WeakPtrFactory一樣, CancelableTaskTracker也是在析構(gòu)的時(shí)候取消所有相關(guān)的任務(wù)。
Cancelable request(DEPRECATED)
因?yàn)檫@個(gè)以后就廢棄了就不翻譯了。。。。

浙公網(wǎng)安備 33010602011771號(hào)