C++ 多線程下的單例模式
阿新 • • 發佈:2017-09-17
!= 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++ 多線程下的單例模式