单例模式
singleton pattern
purpose:使得一个类只能有一个实例,并提供一个全局访问的位置
probliem:遇到实例的频繁创建和销毁时,效率低
solution:创建时候检测实例是否存在
key code:构造函数是私有的
如何选择懒汉和饿汉模式:
特点与选择:
懒汉:在访问量较小时,采用懒汉实现。这是以时间换空间。
饿汉:由于要进行线程同步,所以在访问量比较大,或者可能访问的线程比较多时,采用饿汉实现,可以实现更好的性能。这是以空间换时间。
懒汉式单例模式代码(线程不安全)
顾名思义,该模式只在你需要对象时才会生成单例对象(意思是太懒了,懒汉不到万不得已就不会去实例化类)
缺点:
- 懒汉式是以时间换空间的方式。(在getInstance中new instance然后返回)
- 适用于单线程的环境!
- 不是线程安全的!
- 它不是线程安全的。假设当前有N个线程同时调用getInstance()方法,由于当前还没有对象生成,所以一部分同时都进入步骤 2,那么就会由多个线程创建多个多个singleton对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| 懒汉式单例模式 - 私有静态指针,指向单例 - 私有构造函数 - 公有获取实例的静态函数 # include <iostream> using namespace std;
class singleton{ public: static singleton *getinstance() { if (instance == NULL) 步骤 1 { instance = new singleton(); 步骤 2 return instance; } else { cout << "instance has already initialized" << endl; return instance; } } private: static singleton * instance ; singleton() { cout << "instance initialized" << endl; } }; singleton * singleton::instance = NULL;
int main() { singleton * singleton1 = singleton::getinstance(); singleton * singleton2 = singleton::getinstance(); }
|
饿汉式单例模式代码(线程安全)
顾名思义,该模式在类被加载时就会实例化一个对象。(意思是有就吃,有加载个类的话,饿汉就直接在类里面建立)
- 饿汉式是以空间换时间的方式。(在静态区初始化instance,然后通过getInstance返回)
- 该模式能简单快速的创建一个单例对象,而且是线程安全的(只在类加载时才会初始化,以后都不会)。
- 缺点,就是不管你要不要都会直接创建一个对象,会消耗一定的性能(当然很小很小,几乎可以忽略不计,所以这种模式在很多场合十分常用而且十分简单)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| # include <iostream> using namespace std; class singleton{ public: static singleton *getinstance(){ return instance; }
private: static singleton *instance; singleton() { cout << "instance initialized" << endl; } };
singleton * singleton::instance = new singleton();
int main() { singleton *singleton1 = singleton::getinstance(); singleton *singleton2 = singleton::getinstance(); }
|
注意,这个单例在主函数之前就已经加载了,比如下面主函数输出的结果就是instance initialized。
线程安全的懒汉式单例模式代码
加了一个互斥锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| # include <iostream> # include <mutex> # include <pthread.h> using namespace std;
class singleton{ public: static pthread_mutex_t mutex;
static singleton *getinstance() { pthread_mutex_lock(&mutex); if (instance == NULL) { instance = new singleton(); return instance; } else { cout << "instance has already initialized" << endl; return instance; } pthread_mutex_unlock(&mutex); } private: static singleton * instance ; singleton() { pthread_mutex_init(&mutex,NULL); cout << "instance initialized" << endl; } }; singleton * singleton::instance = NULL; pthread_mutex_t singleton::mutex;
int main() { singleton * singleton1 = singleton::getinstance(); singleton * singleton2 = singleton::getinstance(); }
|
注意:
互斥变量使用特定的数据类型:pthread_mutex_t
线程函数编译时需要添加特殊编译选项:
gcc read.c -lpthread 比如最简单的编译命令,记住要在后加-lpthread选项,
否则会出现undefined reference to ‘pthread_mutex_lock’报错。
参考资料
饿汉式和懒汉式(单例模式详解)
C++ 单例模式(懒汉、饿汉模式)
C++关于锁的总结(一)
windows下std 没有成员 thread、thread not member of std
c++线程中的几种锁
本文许可证
本文遵循 CC BY-NC-SA 4.0(署名 - 非商业性使用 - 相同方式共享) 协议,转载请注明出处,不得用于商业目的。