什么是单例模式?
单例模式是为确保一个类只有一个实例,并为整个系统提供一个全局访问点的一种模式方法。
单例特点:
1 在任何情况下,单例类永远只有一个实例存在。
2 单例需要有能力为整个系统提供这一唯一实例。
示例:打印机,任务管理器等。
实现一(单线程使用,多线程不安全)
#include
代码语言:javascript复制 using namespace std;
class Singleton
{
private:
Singleton(){}
public:
static Singleton* instance()
{
if(_instance == 0)
_instance = new Singleton();
return _instance;
}
private:
static Singleton* _instance;
};
Singleton* Singleton::_instance = 0;
上面这种实现在单线程环境下是没有问题的,可是多线程下就有问题了。
分析:
1 线程A进入函数执行判断语句,这句执行后就挂起了,这时线程A已经认为为NULL,但是线程A还没有创建对象。
2 又有一个线程B进入函数执行判断语句,此时同样认为变量为null,因为A没有创建对象。线程B继续执行,创建了一个对象。
3 稍后,线程A接着执行,也创建了一个新的对象。
4 创建了两个对象!
从上面分析可以看出,需要对变量加上互斥锁:
实现二(多线程安全,加锁代价高)
#include
代码语言:javascript复制 #include
using namespace std;
std::mutex mt;
class Singleton
{
private:
Singleton(){}
public:
static Singleton* instance()
{
mt.lock(); // 加锁
if(_instance == 0)
_instance = new Singleton();
mt.unlock(); // 解锁
return _instance;
}
private:
static Singleton* _instance;
};
Singleton* Singleton::_instance = 0;
上锁后是解决了线程安全问题,但是有些资源浪费。稍微分析一下:每次函数调用时候都需要请求加锁,其实并不需要,函数只需第一次调用的时候上锁就行了。这时可以用DCLP解决。
实现三(双检查锁,由于内存读写导致不安全)
Double-
#include
代码语言:javascript复制 #include
using namespace std;
std::mutex mt;
class Singleton
{
private:
Singleton(){}
public:
static Singleton* instance()
{
if(_instance == 0)
{
mt.lock();
if(_instance == 0)
_instance = new Singleton();
mt.unlock();
}
return _instance;
}
private:
static Singleton* _instance;
public:
int atestvalue;
};
Singleton* Singleton::_instance = 0;
这个版本很不错,又叫“双重检查”Double-Check。下面是说明:
第一个条件是说,如果实例创建了,那就不需要同步了,直接返回就好了。不然,我们就开始同步线程。第二个条件是说,如果被同步的线程中,有一个线程创建了对象,那么别的线程就不用再创建了。
分析
_instance = new Singleton();
为了执行这句代码,机器需要做三样事儿:
1.对象分配空间。
2.在分配的空间中构造对象
3.使指向分配的空间
遗憾的是编译器并不是严格按照上面的顺序来执行的。可以交换2和3.
将上面三个步骤标记到代码中就是这样:
Singleton* Singleton::instance() {
代码语言:javascript复制 if (_instance == 0) {
mt.lock();
if (_instance == 0) {
_instance = // Step 3
operator new(sizeof(Singleton)); // Step 1
new (_instance) Singleton; // Step 2
}
mt.unlock();
}
return _instance;
}
实现四(C 11版本最简洁的跨平台方案)(推荐版本)
Meyers
局部静态变量不仅只会初始化一次,而且还是线程安全的。
代码语言:javascript复制#include
using namespace std;
class Singleton
{
public:
// 注意返回的是引用
static Singleton& getInstance()
{
static Singleton value; //静态局部变量
return value;
}
private:
Singleton() = default;
Singleton(const Singleton& other) = delete; //禁止使用拷贝构造函数
Singleton& operator=(const Singleton&) = delete; //禁止使用拷贝赋值运算符
};
int main()
{
Singleton& s1 = Singleton::getInstance();
cout
[1]: https://xuan.ddwoo.top/index.php/archives/556/
[2]: https://xuan.ddwoo.top/index.php/archives/558/
[3]: https://xuan.ddwoo.top/index.php/archives/549/
[4]: https://xuan.ddwoo.top/index.php/archives/555/
本文共 647 个字数,平均阅读时长 ≈ 2分钟