单例模式共享数据分析
首先来看单例模式的实现:
所谓单例,就是对象的创建只能一次,也就是不能通过构造函数直接创建对象,要通过其他手段,下面请看代码:
// ConsoleApplication6.cpp : 定义控制台应用程序的入口点。 #include "stdafx.h" #include<thread> #include<iostream> #include<list> #include<mutex> using namespace std; class A { private: A() {} static A* Instance; public: static A* GetInstance() { if (Instance == NULL) { Instance = new A; } return Instance; } };
A* A::Instance=NULL; int main() { A * x = A::GetInstance(); A* y = A::GetInstance(); return 0; }
上面虽然企图生成两个对象,但最终结果返回的是一个对象的指针。
上述代码没有考虑对象释放的问题,现对代码进行改进,让其也自动释放不要的内存:
// ConsoleApplication6.cpp : 定义控制台应用程序的入口点。 #include "stdafx.h" #include<thread> #include<iostream> #include<list> #include<mutex> using namespace std; class A { private: A() {} static A* Instance; public: static A* GetInstance() { if (Instance == NULL) { Instance = new A; static A_Guard gl;//在这里生成了一个静态对象,在类A释放的时候,类A_Guard也会被释放 } return Instance; } class A_Guard { public: ~A_Guard() { if (A::Instance!=NULL) { delete A::Instance; A::Instance = NULL; } } }; };
A* A::Instance=NULL;
int main()
{
A * x = A::GetInstance();
A* y = A::GetInstance();
return 0;
}
接下来我开对多个线程来创建该对象,然后调用对象的函数实现多线程技术:
// ConsoleApplication6.cpp : 定义控制台应用程序的入口点。 #include "stdafx.h" #include<thread> #include<iostream> #include<list> #include<mutex> using namespace std; class A { private: A() {} static A* Instance; public: static A* GetInstance() { if (Instance == NULL) { Instance = new A; static A_Guard gl;//在这里生成了一个静态对象,在类A释放的时候,类A_Guard也会被释放 } return Instance; } void Test(int n) { cout << "测试:" << n << endl; } class A_Guard { public: ~A_Guard() { if (A::Instance!=NULL) { delete A::Instance; A::Instance = NULL; } } }; }; A* A::Instance = NULL; void Thread_One(int n) { A* x = A::GetInstance(); x->Test(n); } void Thread_Two(int n) { A* y = A::GetInstance(); y->Test(n); } int main() { thread th_one(Thread_One,1); thread th_two(Thread_Two,2); th_one.join(); th_two.join(); return 0; }
上述的对象可能会被多次构造,扣取上诉代码片段:
1 if (Instance == NULL) 2 { 3 Instance = new A; 4 static A_Guard gl;//在这里生成了一个静态对象,在类A释放的时候,类A_Guard也会被释放 5 }
如果一个线程还没有执行第三行,另外一个线程也进来了,那是不是就不能保证对象只被创建了一次,于是可以试着加把锁试试看,接下来我对代码进行改进:
1 // ConsoleApplication6.cpp : 定义控制台应用程序的入口点。 2 #include "stdafx.h" 3 #include<thread> 4 #include<iostream> 5 #include<list> 6 #include<mutex> 7 using namespace std; 8 mutex mut_one; 9 class A 10 { 11 private: 12 A() {} 13 static A* Instance; 14 public: 15 static A* GetInstance() 16 { 17 unique_lock<mutex> m(mut_one); 18 if (Instance == NULL) 19 { 20 Instance = new A; 21 static A_Guard gl;//在这里生成了一个静态对象,在类A释放的时候,类A_Guard也会被释放 22 } 23 return Instance; 24 } 25 void Test(int n) 26 { 27 cout << "测试:" << n << endl; 28 } 29 30 class A_Guard 31 { 32 public: 33 ~A_Guard() 34 { 35 if (A::Instance!=NULL) 36 { 37 delete A::Instance; 38 A::Instance = NULL; 39 } 40 } 41 }; 42 }; 43 A* A::Instance = NULL; 44 45 void Thread_One(int n) 46 { 47 A* x = A::GetInstance(); 48 x->Test(n); 49 } 50 void Thread_Two(int n) 51 { 52 A* y = A::GetInstance(); 53 y->Test(n); 54 } 55 int main() 56 { 57 thread th_one(Thread_One,1); 58 thread th_two(Thread_Two,2); 59 th_one.join(); 60 th_two.join(); 61 62 return 0; 63 }
这样好像就解决问题了,但真的这样吗?
仔细观察发现,对象的构建只需要一次,也就是说我只需要在对象创建的那时候加锁就可以了,创建好后根本没有必要加锁了,加锁虽然解决了对象创建的问题,但是大大降低了相率,接下来对代码进一步改进:
1 // ConsoleApplication6.cpp : 定义控制台应用程序的入口点。 2 #include "stdafx.h" 3 #include<thread> 4 #include<iostream> 5 #include<list> 6 #include<mutex> 7 using namespace std; 8 mutex mut_one; 9 class A 10 { 11 private: 12 A() {} 13 static A* Instance; 14 public: 15 static A* GetInstance() 16 { 17 if (Instance == NULL) 18 { 19 unique_lock<mutex> m(mut_one); 20 if (Instance == NULL) 21 { 22 Instance = new A; 23 static A_Guard gl;//在这里生成了一个静态对象,在类A释放的时候,类A_Guard也会被释放 24 25 } 26 }29 return Instance;31 } 32 void Test(int n) 33 { 34 cout << "测试:" << n << endl; 35 } 36 37 class A_Guard 38 { 39 public: 40 ~A_Guard() 41 { 42 if (A::Instance!=NULL) 43 { 44 delete A::Instance; 45 A::Instance = NULL; 46 } 47 } 48 }; 49 }; 50 A* A::Instance = NULL; 51 52 void Thread_One(int n) 53 { 54 A* x = A::GetInstance(); 55 x->Test(n); 56 } 57 void Thread_Two(int n) 58 { 59 A* y = A::GetInstance(); 60 y->Test(n); 61 } 62 int main() 63 { 64 thread th_one(Thread_One,1); 65 thread th_two(Thread_Two,2); 66 th_one.join(); 67 th_two.join(); 68 69 return 0; 70 }
多了一个17行了判断条件,与第20行组成双重判断,这样的话只有在最开始才会进入if条件,后面的情况都会跳出if执行else直接返回对象指针。
接下来引入call_once,也是为了解决上诉问题的,但是据说效率比mutex更高,接下来请看代码:
// ConsoleApplication6.cpp : 定义控制台应用程序的入口点。 #include "stdafx.h" #include<thread> #include<iostream> #include<list> #include<mutex> using namespace std; mutex mut_one; once_flag gl_flag;//标记 class A { private: A() {} static A* Instance; public: static A* CreateInstance() { Instance = new A; static A_Guard gl; return Instance; } static A* GetInstance() { call_once(gl_flag,CreateInstance); return Instance; } void Test(int n) { cout << "测试:" << n << endl; } class A_Guard { public: ~A_Guard() { if (A::Instance!=NULL) { delete A::Instance; A::Instance = NULL; } } }; }; A* A::Instance = NULL; void Thread_One(int n) { A* x = A::GetInstance(); x->Test(n); } void Thread_Two(int n) { A* y = A::GetInstance(); y->Test(n); } int main() { thread th_one(Thread_One,1); thread th_two(Thread_Two,2); th_one.join(); th_two.join(); return 0; }
下面说说call_once的用法:
call_once(gl_flag,CreateInstance);表示只调用CreateInstance函数一次(也就是括号中的第二个参数),它需要一个标记(第一个参数)