您的位置:首页 > 编程语言 > C语言/C++

单例模式的简单小结 C++

2016-03-24 11:38 363 查看
单例模式:

保证一个类只有一个实例, 并提供一个访问他的全局访问点

通常当我们需要确保一个类只有一个实例的时候, 应该选用单例模式, 让类自身负责保存他的唯一实例。这个类可以保证没有其他实例被创建, 同时提供一个访问这个唯一实例的一个方法

一般我们可以使用 私有构造方法来实现这个功能, 让外界无法 new 出这个类的对象。

代码如下:

/************************************************************************/
/* 非线程安全的单例模式的实现              线程不安全                             */
/************************************************************************/
class CSingleton{
public:
static int getCount(){ return count; }

static std::shared_ptr<CSingleton> getInstance(){
if (instance == nullptr){
instance = std::shared_ptr<CSingleton>(new CSingleton);
}

return instance;
}

private:
CSingleton(){
count++;
}
static std::shared_ptr<CSingleton> instance;
static int count;
};

int CSingleton::count = 0;
std::shared_ptr<CSingleton> CSingleton::instance(nullptr);


但是, 这个方法是有问题的, 他对线程是不安全的。一般涉及static 的变量和函数调用都有这样一个线程安全问题。简单解释下, 当两个线程并发访问的时候, 当一个线程进入

if (instance == nullptr)


并且执行完判断, 但是还没来得及将instance 赋予一个新的值的时候, 另一个线程也执行了这个判断, 最终的效果就是出现的两个类对象, count == 2了。

就好像,你去蹲坑, 刚进去, 另一个兄弟也进来了。。。。

我们可以使用互斥锁解决这个问题, 代码如下:

/************************************************************************/
/* 线程安全的单例模式的实现1          单层锁   每次调用都要加锁, 费资源                             */
/************************************************************************/
class CSingleton1{
public:
static int getCount(){ return count; }

static std::shared_ptr<CSingleton1> getInstance(){
g_mutex.lock();
if (instance == nullptr){
instance = std::shared_ptr<CSingleton1>(new CSingleton1);
}
g_mutex.unlock();

return instance;
}

private:
CSingleton1(){
count++;
}
static std::shared_ptr<CSingleton1> instance;
static int count;
static std::mutex g_mutex;
};

int CSingleton1::count = 0;
std::shared_ptr<CSingleton1> CSingleton1::instance(nullptr);
std::mutex CSingleton1::g_mutex;


但是呢, 我每次调用这个方法的时候, 都会进行一次加锁的过程, 这个很浪费资源, 很自然的我们想到了双重锁定的方法:

/************************************************************************/
/* 线程安全的单例模式的实现2          双重锁       避免了每次调用都要加锁的需求                        */
/************************************************************************/
class CSingleton2{
public:
static int getCount(){ return count; }

static std::shared_ptr<CSingleton2> getInstance(){
if (instance == nullptr){
g_mutex.lock();
if (instance == nullptr){
instance = std::shared_ptr<CSingleton2>(new CSingleton2);
}
g_mutex.unlock();
}

return instance;
}

private:
CSingleton2(){
count++;
}
static std::shared_ptr<CSingleton2> instance;
static int count;
static std::mutex g_mutex;
};

int CSingleton2::count = 0;
std::shared_ptr<CSingleton2> CSingleton2::instance(nullptr);
std::mutex CSingleton2::g_mutex;


另外还有一种解决的思路, 就是我在类初始化的时候, 就把这个对象给创建出来, 以后每次调用的时候, 就不需要处理复杂的线程安全问题了。

/************************************************************************/
/* 线程安全的单例模式的实现3  静态初始化                                */
/************************************************************************/
class CSingleton3{
public:
static int getCount(){ return count; }

static std::shared_ptr<CSingleton3> getInstance(){
return instance;
}

private:
CSingleton3(){
count++;
}
static std::shared_ptr<CSingleton3> instance;
static int count;
static std::mutex g_mutex;
};

int CSingleton3::count = 0;
std::shared_ptr<CSingleton3> CSingleton3::instance(new CSingleton3);


稍微总结一下:

静态初始化的方式, 我们一般将他称为是饿汉式单例类, 他会提前占用系统资源, 而懒汉式单例类, ie, 在第一次被引用的时候,才调用自己的实例化, 会面临多线程安全的问题。需要根据需求选择不同的单例方式。

最后给出main.cpp

#include "singleton.h"
#include <thread>
#include <iostream>

using namespace std;

int main2(){
auto s1 = CSingleton2::getInstance();
auto s2 = CSingleton2::getInstance();

cout << "当前实例个数:\t" << CSingleton2::getCount() << endl;

system("pause");
return 0;
}

void getSingleton(){
auto s1 = CSingleton3::getInstance();
}

int main(){
thread t1(getSingleton);
thread t2(getSingleton);
t2.join();
t1.join();

cout << "当前实例个数:\t" << CSingleton3::getCount() << endl;

system("pause");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: