2011-11-21 26 views
2

我有我自己的多線程服務,它處理一些命令。該服務由命令解析器,帶有隊列的工作線程和一些緩存組成。我不想關注每個對象的生命週期,所以我使用shared_ptr的非常廣泛的。每個組件都以自己的方式使用shared_ptr:用於異步方案的shared_ptr策略是什麼?

  • 命令解析器創建shared_ptr's並將它們存儲在緩存中;
  • worker將shared_ptr綁定到仿函數並將它們放入隊列。
  • 緩存臨時或永久保存一些shared_ptr的。
  • 由shared_ptr引用的數據也可以包含其他一些shared_ptr。

還有另一個基礎服務(例如命令接收器和發送器)具有相同的結構,但使用他自己的緩存,工人和shared_ptr's。它與我的服務無關,由其他開發人員維護。

這是一個完整的惡夢,當我試圖跟蹤所有的shared_ptr依賴關係以防止交叉引用。

有沒有一種方法來指定一些shared_ptr「接口」或「政策」,所以我會知道哪些shared_ptr我可以安全地傳遞到底層服務,而無需檢查代碼或與開發人員交互?策略應該包含shared_ptr擁有周期,例如,自dispatch()函數調用並直到某個其他函數調用之後,worker將持有帶有綁定shared_ptr的函子,而緩存保存自從緩存構造函數調用後的shared_ptr,直到緩存析構函數調用。

特別是,我很關心關機的情況,當應用程序可能會凍結而等待線程加入。

回答

1

沒有銀子彈......而shared_ptr當然不是其中之一。


我的第一個問題是:你是否需要所有這些共享指針?

避免循環引用的最佳方法是定義每個對象的生命週期策略並確保它們是兼容的。這可以很容易地證明:

  • 你給我一個參考,我希望對象住整個函數調用,但沒有更多的
  • 你給我一個unique_ptr,我現在的對象負責
  • 你給我一個shared_ptr,我希望能夠將手柄保持對象自己沒有你

不利影響現在,有罕見確實需要使用shared_ptr的情況。緩存指示讓我認爲它可能是你的情況,至少對於一些使用。

在這種情況下,您可以(至少非正式地)實施分層方法。

  • 定義數量的層,從0(基),以無限
  • 每個類型的對象是歸因於一個層時,幾種類型可以共享相同的層
  • A類型的對象可能只持有shared_ptrB如果類型的對象,且僅當Layer(A) > Layer(B)

請注意,我們明確禁止兄弟關係。有了這個方案,就不會形成任何引用的循環。事實上,我們獲得了一個DAG(有向無環圖)。

現在,當創建一個類型時,它必須被賦予一個圖層編號,並且必須記錄下來(最好在代碼中)。

的對象可能變層的,但是:

  • 如果層數減少,那麼你必須重新審視其持有的引用(容易)
  • 如果層數的增加,則必須重新檢查所有對它的引用(硬)

注意:按照慣例,不能容納任何引用的對象的類型通常在圖層0中。注2:我首先在Herb Sutter的一篇文章中偶然發現了這個約定,並將它應用於Mutexes並試圖防止死鎖。這是對當前問題的適應。


這可以更自動,只要你準備工作,你現有的代碼庫來執行位(由編譯器)。

我們創建一個新SharedPtr類知道我們分層方案:

template <typename T> 
constexpr unsigned getLayer(T const&) { return T::Layer; } 


template <typename T, unsigned L> 
class SharedPtrImpl { 
public: 
    explicit SharedPtrImpl(T* t): _p(t) 
    { 
    static_assert(L > getLayer(std::declval<T>()), "Layering Violation"); 
    } 

    T* get() const { return _p.get(); } 

    T& operator*() const { return *this->get(); } 
    T* operator->() const { return this->get(); } 

private: 
    std::shared_ptr<T> _p; 
}; 

可以在這樣一個SharedPtr舉行靜態給出了層,我們用一個基類來幫助我們每種類型:

template <unsigned L> 
struct LayerMember { 
    static unsigned const Layer = L; 

    template <typename T> 
    using SharedPtr<T> = SharedPtrImpl<T, L>; 
}; 

而現在,我們可以很容易地使用它:

class Foo: public LayerMember<3> { 
public: 

private: 
    SharedPtr<Bar> _bar; // statically checked! 
}; 

然而,這種編碼方法有一點涉及,我認爲約定可能已經足夠;)

0

你應該看看weak_ptr。它補充了shared_ptr,但不保持對象的活動狀態,所以當你可能有循環引用時非常有用。