Chormium執行緒模型及應用指南

horky發表於2016-01-09

核心概念

設計上遵循以下原則:

1 不要在UI執行緒做任何阻塞式的I/O操作,以及其它耗時的操作,通過訊息傳遞把各種操作傳給相應用途的執行緒去做。

2 不鼓勵執行緒加鎖機制和執行緒安全物件。物件只存在一個執行緒,通過訊息來實現執行緒之間的通訊,執行緒之間不互相阻塞。通過callback物件實現跨執行緒請求。

一般情況下,我們應該利用一個現有的執行緒,儘量不要建立新的執行緒。UI執行緒實際上會被設定為不允許I/O操作,並且不允許等待。 


執行緒列表

核心執行緒列表

所屬程式執行緒名稱型別職責實現
 BrowserUI / BrowserThreadJavaBrowser的主執行緒content::BrowserThreadImpl
 Browser Chrome_DBThreadNative負責資料庫(SQLite)相關的操作,很多功能的實現會用到該執行緒。content::BrowserThreadImpl
 Browser Chrome_FileThreadNative檔案的建立、刪除、讀寫等。content::BrowserThreadImpl
 Browser Chrome_FileUserBlockingThreadNative用於讀取與使用者互動有關的資料,需要快速的響應。看到net log模組和appcache有用。content::BrowserThreadImpl
 Browser Chrome_ProcessLauncherThreadNative用於啟動和終止子程式。content::BrowserThreadImpl
 Browser Chrome_CacheThreadNative  
    content::BrowserThreadImpl
 Browser IndexedDBNativeIndexDB儲存執行緒。base::Thread
GPU Chrome_InProcGpuThreadNativeGPU的單程式版本實現。content::InProcessGpuThread
Child ProcessesChrome_ChildIOThreadNative子程式的IO執行緒實現。base::Thread
RendererChrome_InProcRendererThreadNativeRenderer程式的單程式版本實現。content::InProcessRendererThread

 

其它執行緒

執行緒名稱Module實現說明
CookieMonsterClientCAWbase::Thread 
CookieMonsterBackendCAWbase::Thread 
CookieSyncManagerWebViewRunnable 
Chrome_libJingle_WorkerThreadBrowserbase::Thread 
Blink Heap Marker Thread (*)Blinkbase::Thread 
Blink GC Sweeper (*)Blinkbase::Thread 
HTMLParserThreadBlinkbase::Thread 
AsyncTransferThreadgpugpu::TransferThread 
BrowserBlocking WorkerBrowserbase::SequencedWorkerPool詳見:Chromium中應用C/C++併發技術要點
SimpleCache Workernetbase::SequencedWorkerPool 
Network File Threadnetbase::Thread 

  

執行緒結構

(以單程式模型說明)

Android下執行緒的訊息結構


Chromium的執行緒結構


各個類的職責說明:

職責說明
RunLoop

一個輔助類,主要封裝訊息迴圈 MessageLoop 類,其本
身沒有特別的功能,主要提供一組公共介面被呼叫,其實質是呼叫 MessageLoop 類的介面和實現

MessageLoop

主訊息迴圈,原理上講,它應該可以處理三種型別的訊息,包括支援不同平臺的訊息。

事實上,如果讓它處理所有這些訊息,這會讓其程式碼結構複雜不清難以理解。訊息迴圈只需要三種型別:

  • 一種僅能處理自定義任務
  • 一種能處理自定義任務和 IO 操作
  • 一種是能處理自定義任務和 UI 訊息。


很自然地,Chromium 定義一個基類 MessageLoop 用於處理自定義任務,兩個子類對應於第二和第三種型別。

對於第二和第三種 MessageLoop 型別,它們除了要處理任務外,還要處理平臺相關的訊息,為了結構清晰,
chromium 定義一個新的基類及其子類來負責處理它們,這就是 MessagePump。MessagePump 的每個子類針對不同平臺
和不同的訊息型別。

MessagePump

一個抽象出來的基類,可以用來處理上面所列的第二和第三種訊息型別。對於每個平臺,它們有不同的
MessagePump 的子類來對應,這些子類被包含在 MessageLoopForUI 和 MessageLoopForIO 類中。

摘自:<<理解WebKit和Chromium>> 


Browser端執行緒結構



Browser端拋轉執行緒訊息,主要是基於BrowserThread提供的方法來完成的,如下:

// 檢測所在的執行緒
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
 
// 拋轉任務到UI執行緒執行
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
        base::Bind(&AwLoginDelegate::HandleHttpAuthRequestOnUIThread,
                   this, (count->auth_attempts_ == 0)));
 
// 拋轉任務到IO執行緒
BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
      base::Bind(&AwLoginDelegate::ProceedOnIOThread,
                 this, user, password));

 

 

Render端的執行緒結構 



Renderer端拋轉訊息,主要是基於MessageLoopProxy來完成。如下:

base::Closure closure =
        base::Bind(&CompositorOutputSurface::ShortcutSwapAck,
                   weak_ptrs_.GetWeakPtr(),
                   output_surface_id_,
                   base::Passed(&frame->gl_frame_data),
                   base::Passed(&frame->software_frame_data));
base::MessageLoopProxy::current()->PostTask(FROM_HERE, closure);
 
// input_event_filter.cc中的示例
io_loop_->PostTask(FROM_HERE,
                     base::Bind(&InputEventFilter::SendMessageOnIOThread,
                                this,
                                base::Passed(&message)));
 
// Blink platform implemetation
base::MessageLoopProxy::current()->PostTask(
      FROM_HERE,
      base::Bind(&PlatformEventObserverBase::SendFakeDataForTesting,
                 base::Unretained(observer), data));

 

gpu與Browser/Renderer的互動




執行緒安全

對於Java及Android的執行緒安全不再展開,可以參考附件的資料:<<Efficient Android Threading Asynchronous Processing Techniques for Android Applications>>

關於Java則推薦《Java併發實戰》。

最常用的形式,將使用的物件定義為base::RefCountedThreadSafe。保證引用的物件不會被提前析構。

對於一些非執行緒安全的類可以使用NonThreadSafe提供Debug模式下執行緒安全確認。也可以應用ThreadCollisionWarner/ThreadChecker 確保執行執行緒與設計一致。

詳見:Chromium中應用C/C++併發技術要點

參考: 如何安全的使用PostTask


任務的取消

除了任務按需要取消外,如果在宿主類析構後執行就可能導致崩潰。目前使用兩種方式保證任務的取消:

    WeakPtrFactory (WeakPtr)和CancelableTaskTracker, 它們析構時也會自動將任務取消。

CancelableTaskTracker可以參考Chromium官網的說明或是在FaviconCache中的應用。

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).get(),
        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_;
  ...
};

 

對於WeakPtr,Chromium已經封裝了一個WeakptrFactory供使用。可以參考GpuBrowserCompositorOutputSurface中的使用。使用方式比較簡單,但沒有CancelableTaskTracker通用。

下面是一個簡單的示例(使用WeakPtrFactory<>最大的好處是不用修改類的定義.)

class MyObject {
 public:
  MyObject() : 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_;
};
*非執行緒安全,可以跨執行緒傳遞,但必須在一個執行緒上使用這個WeakPtr,即只能在執行在相同執行緒的任務上使用這個機制。
*類中WeakPtrFactory<Foo> weak_factory_的成員需要放在所有其他成員的後面,確保其他成員的解構函式執行的時候WeakPtrs還是無效的。

  

關於WeakPtr的進一步解釋可以參考: Chromium中的weak_ptr,以及 關於SupportWeakPtr與WeakPtrFactory的選擇


相關文章