原文链接:http://blog.chinaunix.net/uid-29686891-id-4284971.html
我想关于Singleton模式的实现和资料很多很多,这里为什么专门拿出来写一写,还是因为个人觉得要想把单例模式写好还真不是一件容易的事情。
其中涉及到不少编译和底层的知识。这里以Linux平台为例,这是因为本人对windows下的编程实在不太熟悉。
本文所有代码均上传至github仓库:https://github.com/kevin-shanghai/Programming_Practice
如果有兴趣,可以随意获取,本代码仅用于测试,如用于生产环境发生任何问题,本人概不负责。
本文针对于线程安全的Singleton实现,并基于模板,这样通用性更好,否则对于每个类都要实现自己的单例模式。
一般的单线程下的Singleton实现非常简单,用一个静态成员函数返回该类的一个实例就行了,但是遇到多线程,事情就麻烦了不少。
废话少说,下面看下具体代码:
/**********************************************
2. ******* g++ test.cpp -lpthread****************
3. * *******************************************/
4. #include <iostream>
5. #include <pthread.h>
6. using namespace std;
7. template <class T>
8.class Singleton
9.{
10.public:
11. Singleton()
12. {
13.
14. }
15. static T* GetInstance()
16. {
17. if(m_pInstance == NULL)
18. {
19. pthread_mutex_lock(&mutex);
20. if(m_pInstance == NULL)
21. {
22. T* temp = new T;
23. m_pInstance = temp;
24. }
25. pthread_mutex_unlock(&mutex);
26. }
27. return m_pInstance;
28. }
29.private:
30. static T* m_pInstance;
31. static pthread_mutex_t mutex;
32.};
33.
34. template<class T>
35. T* Singleton<T>::m_pInstance = NULL;
36.
37. template<class T>
38. pthread_mutex_t Singleton<T>::mutex;
39.
40.class Test:public Singleton<Test>
41.{
42.public:
43. Test()
44. {
45. cout<<"Test::Test()."<<endl;
46. }
47.};
48.
49. void* thread1(void*)
50.{
51. cout<<"In thread1."<<endl;
52. Test* test1 = Test::GetInstance();
53.}
54.
55. void* thread2(void *)
56.{
57. cout<<"In thread2."<<endl;
58. Test* test2 = Test::GetInstance();
59.}
60.
61.int main()
62.{
63. const unsigned int thread_num = 20;
64. pthread_t thread_id[thread_num];
65. for(unsigned int i = 0; i<thread_num;i++)
66. {
67. pthread_create(&thread_id[i], NULL, thread1, NULL);
68. }
69. for(unsigned int i = 0; i<thread_num;i++)
70. {
71. pthread_create(&thread_id[i], NULL, thread2, NULL);
72. }
73. sleep(1);
74. return 0;
75.}
可以看到上面的代码中使用了double-check机制,也就是对m_pInstance检查了两遍,这样有利于提高性能。还有一点需要注意的是
T* temp = new T; m_pInstance = temp;而没有直接用m_pInstance = new T;这是因为这一语句将会分为三步来执行:
1. 非配内存
2. 用相应的实例初始化该内存区域
3. 将该内存地址赋值给该指针m_pInstance
如果其中第2步骤和第3步或者第一步骤颠倒了,也就是编译器对我们的代码进行了优化,那么赋值给m_pInsannce指针的就是没有初始化的
所以我们用一个临时变量解决这个问题
其实Linux平台下的singleton模式有更简单的实现方式,主要是利用pthread_once这个api,看代码:
#include <iostream>
2. #include <pthread.h>
3. using namespace std;
4. template<class T>
5.class Singleton
6.{
7.public:
8. static T& instance()
9. {
10. pthread_once(&m_Once, &Singleton::init);
11. return *m_tValue;
12. }
13.
14.private:
15. Singleton();
16. ~Singleton();
17. static void init()
18. {
19. m_tValue = new T();
20. }
21.
22.private:
23. static pthread_once_t m_Once;
24. static T* m_tValue;
25.};
26.
27. template<class T>
28. pthread_once_t Singleton<T>::m_Once = PTHREAD_ONCE_INIT;
29.
30. template<class T>
31. T* Singleton<T>::m_tValue = NULL;
32.
33.
34.class Test
35.{
36.public:
37. Test()
38. {
39. cout<<"In Test::Test()."<<endl;
40. }
41.};
42.
43. void* thread_func(void*)
44.{
45. Test& t = Singleton<Test>::instance();
46.}
47.
48.int main(int argc, char const *argv[])
49.{
50.// Test& t = Singleton<Test>::instance();
51. pthread_t thread_id[10];
52. for(int i=0;i<10;i++)
53. {
54. pthread_create(&thread_id[i], NULL, thread_func, NULL);
55. }
56. return 0;
57.}
其实这种方式在Linux平台下比较简单,pthread_once保证Init函数只执行一次,建议Linux下大家用这种方法来实现。
这种单件模式比较通用,而且在多线程环境下也很稳定,如果大家要实现跨平台的singleton模式,那么可以加上相应平台的
创建线程的api就可以了。