单例类:
(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++创建对象时,会执行分配内存,调用构造,赋值操作三部分完成,然而现代CPU和编译器高并发下可能
会进行乱序重排操作,因而创建对象new CSingleton的第二步可能会晚于第三步进行指令调用,因而导致出现未定义的的行为,固需要引入内存栅栏来确保指令运行的同步性。(理解下,具体实现还需要探究)
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静态局部对象被构造的过程中是线程安全的,但是其拥有的成员变量则不是线程安全的,看print函数打印出来的结果print和s_num则是乱序的。
因此我们增加个简单的锁,
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 };
看结果,按顺序打印出来:
继续C++11的学习,加油!!!
书到用时方恨少,是非经过不知难。 博观而约取,厚积而薄发。@karllen 每天进步一点点。
http://www.cnblogs.com/Forever-Kenlen-Ja/p/7050300.html