2010-02-02 110 views
2

我有一個在多個項目之間共享的類,它的一些用途是單線程的,有些是多線程的。單線程用戶不需要互斥鎖的開銷,多線程用戶不希望自己進行鎖定,並希望能夠選擇以「單線程模式」運行。所以我希望能夠在運行時選擇真正的「虛擬」互斥體。在運行時選擇互斥體或虛擬互斥體

理想情況下,我會有一個shared_ptr<something>並分配真實或虛假的互斥對象。然後我會「鎖定」這個,而不考慮它裏面的內容。

unique_lock<something> guard(*mutex); 
... critical section ... 

現在有一個signals2::dummy_mutex,但它不與boost::mutex都有一個共同的基類。

那麼,什麼是一個優雅的方式來選擇真正的互斥體和啞互斥體(信號2中的一個或其他),而不會使鎖定/守衛代碼比上面的示例更復雜?

而且,你所指出的替代品之前:

  • 我可以選擇在編譯時實現,但預處理器宏是醜陋和維護項目配置對我們來說是痛苦的。
  • 多線程環境中的類的用戶不希望承擔鎖定類的使用的責任,而不是讓類在內部自己執行鎖定。
  • 有太多的API和現有的使用「線程安全包裝」的用法是一個實際的解決方案。

回答

4

這樣的事情呢? 它未經測試,但應該接近OK。 如果你的互斥鎖支持正確的構造,你可以考慮讓模板類保持一個值而不是指針 。否則,你可以專門化MyMutex類來獲得價值行爲。

而且它沒有被小心複製或銷燬。我離開,作爲一個練習留給讀者;)(shared_ptr的或存儲的值,而不是一個指針應解決這個問題)

哦,代碼會使用RAII更好,而不是顯式鎖定/解鎖...但這是一個不同的問題。我假設你的代碼中的unique_lock是什麼?

struct IMutex 
{ 
    virtual ~IMutex(){} 
    virtual void lock()=0; 
    virtual bool try_lock()=0; 
    virtual void unlock()=0; 
}; 

template<typename T> 
class MyMutex : public IMutex 
{ 
    public: 
    MyMutex(T t) : t_(t) {} 
    void lock() { t_->lock(); } 
    bool try_lock() { return t_->try_lock(); } 
    void unlock() { t_->unlock(); } 
    protected: 
    T* t_; 
}; 

IMutex * createMutex() 
{ 
    if(isMultithreaded()) 
    { 
    return new MyMutex<boost::mutex>(new boost::mutex); 
    } 
    else 
    { 
    return new MyMutex<signal2::dummy_mutex>(new signal2::dummy_mutex); 
    } 
} 


int main() 
{ 
    IMutex * mutex = createMutex(); 
    ... 
    { 
    unique_lock<IMutex> guard(*mutex); 
    ... 
    } 

} 
+1

添加一個try_lock()方法,它將符合可鎖定概念以與unique_lock類一起使用。請參閱:http://www.boost.org/doc/libs/1_41_0/doc/html/thread/synchronization.html#thread.synchronization.mutex_concepts.lockable – 2010-02-02 04:19:41

+0

@Jason Thx更新了這樣做。 – 2010-02-02 04:34:56

+0

+1。這基本上是「外部多態性」,如下所述:http://www.cs.wustl.edu/~schmidt/PDF/External-Polymorphism.pdf。 – Void 2010-02-02 04:56:57

0

這不夠嗎?

class SomeClass 
    { 
    public: 
     SomeClass(void); 
     ~SomeClass(void); 
     void Work(bool isMultiThreaded = false) 
     { 
      if(isMultiThreaded) 
      { 
       lock // mutex lock ... 
       { 
        DoSomething 
       } 
      } 
      else 
      { 
       DoSomething(); 
      } 
     } 
    }; 
+0

我已經有大量的使用RAII鎖定我描述的圖案的地方。將它們全部改爲使用這種冗長的包裝並不實際。 – 2010-02-02 18:21:52

3

由於兩個互斥類signals2::dummy_mutexboost::mutex不同意,你可以使用類似「external polymorphism」,讓他們去處理多態共同的基類。然後,您可以將它們用作鎖定strategies以用於常見的互斥/鎖定界面。這樣可以避免在鎖定實現中使用「if」語句。

注意:這基本上是Michael提出的解決方案實現的。我會建議與他的答案。

0

通常,只有資源在多個進程之間共享時才需要互斥鎖。如果對象的實例對於(可能是多線程)進程是唯一的,那麼關鍵部分通常更合適。

在Windows中,關鍵部分的單線程實現是虛擬的一個。不知道你使用的是什麼平臺。

1

你有沒有聽說過Policy-based Design

您可以定義一個Lock Policy界面,用戶可以選擇她希望的策略。爲了便於使用,使用編譯時變量來精簡「默認」策略。

#ifndef PROJECT_DEFAULT_LOCK_POLICY 
#define PROJECT_DEFAULT_LOCK_POLICY TrueLock 
#endif 

template <class LP = PROJECT_DEFAULT_LOCK_POLICY> 
class MyClass {}; 

通過這種方式,用戶可以選擇自己用一個簡單的編譯時間開關的政策,並可能在同一時間覆蓋它的一個實例;)

+0

這肯定會起作用,並且這是信號2中現有的boost「dummy_mutex」類的使用方式。不幸的是,我不想讓這個類變成一個模板,並將它的所有代碼移動到頭文件中,我試圖最小化現有代碼的變化範圍。 – 2010-02-02 18:21:28

0

僅供參考,這裏是我結束了落實。

我廢除了抽象基類,將它與無操作「虛擬」實現合併。還要注意帶有隱式轉換運算符的shared_ptr衍生類。我認爲有點太棘手,但它可以讓我使用shared_ptr<IMutex>對象,我之前使用boost::mutex對象進行零變更。

頭文件:

class Foo { 
    ... 
private: 
    struct IMutex { 
     virtual ~IMutex()  { } 
     virtual void lock()  { } 
     virtual bool try_lock() { return true; } 
     virtual void unlock() { } 
    }; 
    template <typename T> struct MutexProxy; 

    struct MutexPtr : public boost::shared_ptr<IMutex> { 
     operator IMutex&() { return **this; } 
    }; 

    typedef boost::unique_lock<IMutex> MutexGuard; 

    mutable MutexPtr mutex; 
}; 

實現文件:

template <typename T> 
struct Foo::MutexProxy : public IMutex { 
    virtual void lock()  { mutex.lock(); } 
    virtual bool try_lock() { return mutex.try_lock(); } 
    virtual void unlock() { mutex.unlock(); } 
private: 
    T mutex; 
}; 

Foo::Foo(...) { 
    mutex.reset(single_thread ? new IMutex : new MutexProxy<boost::mutex>); 
} 

Foo::Method() { 
    MutexGuard guard(mutex); 
} 
0

這是我的解決方案:

std::unique_lock<std::mutex> lock = dummy ? 
    std::unique_lock<std::mutex>(mutex, std::defer_lock) : 
    std::unique_lock<std::mutex>(mutex);