LazyInstance顧名思義,就是延遲創造類執行個體。
那chrome是怎麼做到的呢?
為了效能,或者資源,程式中無法立即用到的對象,我們無需立即創造,比如一個介面的菜單對象,當你還沒有點擊菜單之前就創造了,那他一定會影響軟體的啟動效能。
再比如資料庫物件,或者線程...等等,進可能延遲,chrome高效的原因之一,在於調用無數個LazyInstance對象。
研究過chrome的同學應該知道,chrome的管理工作的執行緒模式,他的所有線程(除了主線程),都可以消極式載入的。
作為消極式載入模版類LazyInstance<Type, Traits>它具備哪些特點呢?
1.安全執行緒。
2.LazyInstance不像單一實例模型Singleton,他可以支援一個類型的多個執行個體。
3,為LazyInstance的類型預先分配空間,減少堆空間產生片段。
4.優美的刪除
使用方法:
static LazyInstance<MyClass> my_instance(base::LINKER_INITIALIZED); void SomeMethod() { my_instance.Get().SomeMethod(); // MyClass::SomeMethod() MyClass* ptr = my_instance.Pointer(); ptr->DoDoDo(); // MyClass::DoDoDo }
從上可知:
LazyInstance<T>::Get()返回的是對象引用
LazyInstance<T>::Pointer()返回的是對象指標
接下來我們一一說明一下上面所述的特點是怎麼做到的。
1.安全執行緒
為了安全執行緒LazyInstance聲明了三個標記:
enum {
STATE_EMPTY = 0, //沒有建立執行個體
STATE_CREATING = 1,//正在建立執行個體,這時另一個被建立的常式應該先被Hold住,第一個先建立完後返回
STATE_CREATED = 2//已經建立了,不需要重複創造。
};
消極式載入當然是第一次調用Pointer()方法時建立(對於Get()方法,當然裡面會調用Pointer方法來實現,儘可能重用代碼).
1).Pointer方法首先不用加鎖,快速擷取state_變數是否已經變成STATE_CREATED狀態,如果已經建立了,就直接返回,否則通過加多線程鎖進行check一下(調用NeedInstance方法),是否是正在建立狀態,如果正在被另一個線程建立,則等待他建立完然後直接返回指標。
這裡LazyInstance儘可能地避免了加鎖,只要建立完了一個執行個體後,後續的所有對狀態的判斷都不會涉及加鎖。
2).如果需要建立則進行調用
Traits::New
Chrome提供了預設的Trait以供通用。如果特意為某個類進行聲明new方法,則定義Trait類,並對其New裡面定義相關對象申請方法,然後在LazyInstance第二個模板裡傳進去
3).返回指標
Type* Pointer() { if (!Traits::kAllowedToAccessOnNonjoinableThread) base::ThreadRestrictions::AssertSingletonAllowed(); if ((base::subtle::Acquire_Load(&state_) != STATE_CREATED) && NeedsInstance()) { instance_ = Traits::New(buf_); void (*dtor)(void*) = Traits::Delete; CompleteInstance(this, (dtor == NULL) ? NULL : OnExit); } ANNOTATE_HAPPENS_AFTER(&state_); return instance_; }
2.LazyInstance不像單一實例模型Singleton,他可以支援一個類型的多個執行個體。
通過上面實力可以看出,對每個消極式載入對象進行全域定義即可。
3,為LazyInstance的類型預先分配空間,減少堆空間產生片段。
含有成員變數預先為特定類進行分配空間,如下:
int8 buf_[sizeof(Type)];
這樣調用LazyInstance的時候我們用的都是靜態全域變數,這些都會存放在待用資料地區,而不是堆地區。對於堆、棧,靜態區的一些介紹,參考:http://www.cnblogs.com/avril/archive/2011/04/28/2031886.html
然後通過調用Trait裡New方法,POD( what is POD:http://en.wikipedia.org/wiki/Plain_old_data_structure )類型初始化
如下:
template <typename Type>
struct DefaultLazyInstanceTraits {
...
static Type* New(void* instance) {
return new (instance) Type();
}
...
}
4.優美的刪除
刪除當然也是調用Trait的Delete方法,當然你也可以指定自己定義的Delete方法。
那Delete是什麼時候被調用呢?
Chrome提供了另一個類AtExitManager來管理全域對象,當整個程式結束時,AtExitManager會調用解構函式,此時會調用註冊在AtExitManager裡的所有回呼函數。
當建立完LazyInstance對象後,在上面的CompleteInstance方法裡註冊Delete到AtExitManager執行個體即可顯示釋放記憶體,避免泄露,如下:
void LazyInstanceHelper::CompleteInstance(void* instance, void (*dtor)(void*)) { base::subtle::Release_Store(&state_, STATE_CREATED); if (dtor) base::AtExitManager::RegisterCallback(dtor, instance);}
總結:
通過分析LazyInsance我們可以知道,Chrome是如何最佳化效能的。
1.儘可能的減少鎖的範圍。
2.儘可能消極式載入。
3.儘可能減少記憶體片段。