1. 程式人生 > >C++ 多線程下的單例模式

C++ 多線程下的單例模式

!= ret ati c++ 靜態 出現 建議 進行 多個實例

/*
    代碼中經常會使用到單例模式,單例模式就是隱藏構造函數,提供獲取一個實例的靜態方法。
    但是在多線程場景下,單例模式會有一些不同。例如Config類的instance方法如下
    */
    //獲取一個實例(對外接口)
    static Config * instance()
    {
        if (NULL == m_instance)
        {
            //加鎖(多線程場景下)
            m_mutex.acquire();
            if (NULL == m_instance)
            {
                m_instance 
= new Config(); /* 類似於這個類的初始化,我是不建議放在這裏的, 因為如果init方法執行失敗,但是實例仍然不為NULL, 建議在main函數中第一次調用instance方法時,執行init方法初始化實例 (註意init方法也是只能執行一次的,請考慮多線程場景) init方法放在這個純粹是為了解釋這個場景下單例的使用 */ m_instance
->init(); } //解鎖 m_mutex.release(); } return m_instance; }
/*
如果在instance方法中不加鎖,在多線程的場景下,有可能創建出多個實例。
instance方法在加鎖之後,還是有問題的。
假設線程A正在執行m_instance->init()方法(init方法執行的時間很長),此時線程B開始執行instance方法,
發現m_instance != NULL(因為線程A已經構造了該實例),那麽線程B就會直接獲取到這個並沒有執行完init方法的m_instance實例,
線程B用這個m_instance去執行操作,就會出現問題。
*/

/*
簡單的改進就是,去掉前邊的判斷,直接加鎖,這樣就避免了該問題,
線程B進行進來的時候因為線程A已經獲取到鎖,線程B會等待,
等到線程A釋放鎖之後(所有初始化操作已經完成),線程B判斷m_instance != NULL,
線程B可以使用m_instance這個實例了
*/
static Config * Config::instance()
{
    //加鎖(多線程場景下)
    m_mutex.acquire();
    if (NULL == m_instance)
    {
        m_instance = new Config();
        m_instance->init();
    }
    //解鎖
    m_mutex.release();
return m_instance; }
/*
但是這樣的改進會出現一個新問題,就是每次調用這個單例就會加鎖判斷,頻繁調用會影響速度
*/

/*
    再次改進方案,使用一個臨時變量構造,初始化,成功後再賦值給m_instance
    這樣避免了多線程操作影響,又不影響速度
    */
    static Config * instance()
    {
        if (NULL == m_instance)
        {
            //加鎖(多線程場景下)
            m_mutex.acquire();
            if (NULL == m_instance)
            {
                Config * pInstance = new Config();
                pInstance->init();
                m_instance = pInstance;
            }
            //解鎖
            m_mutex.release();
        }
        return m_instance;
    }

C++ 多線程下的單例模式