C++11 單例類實現


單例類:

(1) 單例類保證全局只有一個唯一的實例對象。

(2) 單例類保證只有唯一的接口獲取這唯一實例。

非線程安全的單例類舉例:

 1 class CSingleton  2 {  3 public:  4     ~CSingleton(){}  5     static CSingleton * getInstance()  6  {  7         if (m_instance == nullptr)  8  {  9             m_instance = new CSingleton; 10  } 11         return m_instance; 12  } 13     static void delInstance() 14  { 15         if (m_instance) 16  { 17             delete m_instance; 18             m_instance = nullptr; 19  } 20  } 21     void print() 22  { 23         std::cout << "print test" << std::endl; 24  } 25 private: 26  CSingleton(){} 27     CSingleton & operator=(const CSingleton & ) = delete; 28     CSingleton(const CSingleton &) = delete; 29 private: 30     static CSingleton * m_instance; 31 }; 32 
33 CSingleton * CSingleton::m_instance = nullptr;

 

上述單例類面對多線程並發訪問時會出錯。

看如下線程安全的單例類(非C++11實現)

 1 class CSingleton  2 {  3 public:  4     ~CSingleton() {}  5     static CSingleton * getInstance()  6  {  7         if (m_instance == nullptr)  8  {  9             std::lock_guard<std::mutex> lgd(m_mt); 10             if (m_instance == nullptr) 11  { 12                 m_instance = new CSingleton; 13  } 14  } 15         return m_instance; 16  } 17     static void delInstance() 18  { 19         std::lock_guard<std::mutex> lgd(m_mt); 20         if (m_instance) 21  { 22             delete m_instance; 23             m_instance = nullptr; 24  } 25  } 26     void print() 27  { 28         std::cout << "print test" << std::endl; 29  } 30 private: 31  CSingleton() {} 32     CSingleton & operator=(const CSingleton & ) = delete; 33     CSingleton(const CSingleton &) = delete; 34 private: 35     static CSingleton * m_instance; 36     static std::mutex m_mt; 37 }; 38 
39 CSingleton * CSingleton::m_instance = nullptr; 40 std::mutex CSingleton::m_mt;

 

當然絕對的線程安全還是有問題,因為C++創建對象時,會執行1、分配內存,2 調用構造,3 賦值操作三步操作,然而現代CPU和編譯器高並發下可能

會進行亂序重排操作,因而創建對象new CSingleton的第2步可能會晚於第3步進行指令調用,因而導致出現未定義的的行為。

舉例:

線程A : getInstance 判斷 instance是否為空,為空則

線程A : 分配內存  此時CPU亂序指令重排,賦值操作提前

線程B : getInsnace 判斷instance是否為空,非空,則返回

線程B : 使用了未初始化的instacne 出現未定義行為。

線程A : 調用構造函數對instance初始化。

因此要解決上述問題需要引入內存柵欄來確保指令運行的同步性。在CPU指令重排的前提下保持數據的一致性。

C++11支持線程安全的單例類:

C++11的單例模式的實現

 1 class CSingleton  2 {  3 public:  4     ~CSingleton() {}  5     static CSingleton & getInstance()  6  {  7         static CSingleton m_instance;  8         return m_instance;  9  } 10     void print() 11  { 12         std::cout << "print test" << std::endl; 13  } 14 };

 

返回靜態局部對象的引用,C++11中是線程安全的。

驗證一下:

 1 class CStatic  2 {  3 public:  4  CStatic()  5  {  6         std::cout << "construct begin" << std::endl;  7         Sleep(5000);  8         std::cout << "construct end" << std::endl;  9  } 10     void print() 11  { 12         std::cout << "print" << std::endl; 13         std::cout << s_num++ << std::endl; 14  } 15     static int s_num; 16     static std::mutex s_mt; 17 };
 1 int CStatic::s_num = 0;  2 std::mutex CStatic::s_mt;  3 
 4 //  5 void thread_func()  6 {  7     static CStatic st;  8  st.print();  9 } 10 
11 int main() 12 { 13     std::vector<std::thread> vecThread; 14     for (auto i = 0; i< 8; i++) 15  { 16  vecThread.push_back(std::thread(thread_func)); 17  } 18     for (auto i = 0; i< 8; i++) 19  { 20  vecThread[i].join(); 21  } 22     // 23     system("pause"); 24     return 0; 25 }

 

首先我們創建一個CStatic類,然后創建8個線程來啟動thread_func(),thread_func()初始化了一個靜態CStatic對象,(靜態局部變量僅被初始化一次)

然后接着運行。我們發現,當首個線程初始化CStatic時,其他線程都是被阻塞的,從構造函數的begin和end中可以看到,我們故意讓其停留5s,

如下圖,其他線程都是在st被初始化之后才運行。

 

所以CStatic靜態局部對象被構造的過程中是線程安全的,但是其擁有的成員變量則不是線程安全的。

因此我們增加個簡單的鎖,

 1 class CStatic  2 {  3 public:  4  CStatic()  5  {  6         std::cout << "construct begin" << std::endl;  7         Sleep(5000);  8         std::cout << "construct end" << std::endl;  9  } 10     void print() 11  { 12         std::lock_guard<std::mutex> lgd(s_mt); 13         std::cout << "print" << std::endl; 14         std::cout << s_num++ << std::endl; 15  } 16     static int s_num; 17     static std::mutex s_mt; 18 };

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM