2012-04-05 26 views
2

如果我必須在C++中編寫單例類,我將使用一個靜態變量,即私有構造函數&一個返回類的對象的公共靜態函數。但是在多線程環境中,代碼會有問題。爲了避免多個線程同時訪問同一個變量,是Boost線程最佳的同步機制嗎?我的意思是在資源上設置/取消鎖定/互斥鎖。在C++標準庫中還有其他內置內容,我不需要下載提升,編譯內容等?我聽說過C++ Ox,但不太瞭解。Singleton Synchronization C++

回答

7

C++ 98/03根本不支持線程。如果您使用的是C++ 98或03編譯器,那麼您幾乎堅持使用Boost或某些(或多或少)特定於操作系統的操作系統,比如pthread或Win32的線程原語。

C++ 11有一個相當完整的線程支持庫,互斥鎖,鎖,線程本地存儲等

我覺得有必要指出的是,然而,它可能是更好的備份和多做一點思考你是否需要/想要一個Singleton。說得很好,單身模式在很大程度上已經失寵。

編輯:重讀這個,我跳過了一個我想說的事情:至少當我使用它們時,任何/所有單例都在任何輔助線程啓動之前完全初始化。這使得初始化時對線程安全性的擔憂完全沒有實際意義。我想在你啓動輔助線程之前可能會有一個你不能初始化的單例,所以你需要處理這個問題,但至少從它的角度來說,我覺得這是一個相當不尋常的例外, /如果絕對必要。

+0

我還沒有看到任何暗示單身模式在實際從業者中不受歡迎的事情。 (當然,它從來沒有被廣泛使用過,只是在適當的時候,這種情況並不常見) – 2012-04-05 08:44:05

+0

我見過許多'Singleton's,但沒有一個是必需的。只需製作一個並傳遞給它。 – 2012-04-05 09:42:50

+0

粗略地說:一個'Singleton'通常是一個設計錯誤。雖然...我同意記錄方案很方便(遠比在每個函數中傳遞Logger對象更重要)。 – 2012-04-05 11:50:55

0

對於我實現使用C++ 11單身最好的辦法是:

class Singleton 
{ 
public: 
static Singleton & Instance() 
{ 
    // Since it's a static variable, if the class has already been created, 
    // It won't be created again. 
    // And it **is** thread-safe in C++11. 

    static Singleton myInstance; 

    // Return a reference to our instance. 
    return myInstance; 
} 

// delete copy and move constructors and assign operators 
Singleton(Singleton const&) = delete;    // Copy construct 
Singleton(Singleton&&) = delete;     // Move construct 
Singleton& operator=(Singleton const&) = delete; // Copy assign 
Singleton& operator=(Singleton &&) = delete;  // Move assign 

// Any other public methods 

protected: 
Singleton() 
{ 
    // Constructor code goes here. 
} 

~Singleton() 
{ 
    // Destructor code goes here. 
} 

// And any other protected methods. 
} 

這是一個C++ 11功能,但用這種方式,您可以創建一個線程安全的辛格爾頓。根據新標準,再也不需要關心這個問題了。對象初始化將僅由一個線程完成,其他線程將一直等待完成。或者你可以使用std :: call_once。

如果你想獨佔訪問單身人士的資源,你必須在這些功能上使用鎖。

不同類型的鎖:

使用atomic_flg_lck:

class SLock 
{ 
public: 
    void lock() 
    { 
    while (lck.test_and_set(std::memory_order_acquire)); 
    } 

    void unlock() 
    { 
    lck.clear(std::memory_order_release); 
    } 

    SLock(){ 
    //lck = ATOMIC_FLAG_INIT; 
    lck.clear(); 
    } 
private: 
    std::atomic_flag lck;// = ATOMIC_FLAG_INIT; 
}; 

使用原子:

class SLock 
{ 
public: 
    void lock() 
    { 
    while (lck.exchange(true)); 
    } 

    void unlock() 
    { 
    lck = true; 
    } 

    SLock(){ 
    //lck = ATOMIC_FLAG_INIT; 
    lck = false; 
    } 
private: 
    std::atomic<bool> lck; 
}; 

使用互斥:

class SLock 
{ 
public: 
    void lock() 
    { 
    lck.lock(); 
    } 

    void unlock() 
    { 
    lck.unlock(); 
    } 

private: 
    std::mutex lck; 
}; 

只是爲了的Windows

class SLock 
{ 
public: 
    void lock() 
    { 
    EnterCriticalSection(&g_crit_sec); 
    } 

    void unlock() 
    { 
    LeaveCriticalSection(&g_crit_sec); 
    } 

    SLock(){ 
    InitializeCriticalSectionAndSpinCount(&g_crit_sec, 0x80000400); 
    } 

private: 
    CRITICAL_SECTION g_crit_sec; 
}; 

原子和atomic_flg_lck保持在一個旋轉計數的線程。 互斥體只是睡覺的線程。如果等待時間太長,可能會更好地睡眠線程。最後一個「CRITICAL_SECTION」將線程保持在旋轉計數直到消耗時間,然後線程進入休眠狀態。

如何使用這些關鍵部分?

unique_ptr<SLock> raiilock(new SLock()); 

class Smartlock{ 
public: 
    Smartlock(){ raiilock->lock(); } 
    ~Smartlock(){ raiilock->unlock(); } 
}; 

使用raii成語。構造函數鎖定關鍵部分和析構函數以解鎖它。

class Singleton { 

    void syncronithedFunction(){ 
     Smartlock lock; 
     //..... 
    } 

} 

該實現是線程安全和異常安全的,因爲可變鎖被保存在堆棧中,以便當所述功能範圍結束(的功能或異常結束)的析構函數將被調用。

我希望你覺得這有幫助。

謝謝!