单例模式的简单小结 C++
2016-03-24 11:38
363 查看
单例模式:
保证一个类只有一个实例, 并提供一个访问他的全局访问点
通常当我们需要确保一个类只有一个实例的时候, 应该选用单例模式, 让类自身负责保存他的唯一实例。这个类可以保证没有其他实例被创建, 同时提供一个访问这个唯一实例的一个方法。
一般我们可以使用 私有构造方法来实现这个功能, 让外界无法 new 出这个类的对象。
代码如下:
但是, 这个方法是有问题的, 他对线程是不安全的。一般涉及static 的变量和函数调用都有这样一个线程安全问题。简单解释下, 当两个线程并发访问的时候, 当一个线程进入
并且执行完判断, 但是还没来得及将instance 赋予一个新的值的时候, 另一个线程也执行了这个判断, 最终的效果就是出现的两个类对象, count == 2了。
就好像,你去蹲坑, 刚进去, 另一个兄弟也进来了。。。。
我们可以使用互斥锁解决这个问题, 代码如下:
但是呢, 我每次调用这个方法的时候, 都会进行一次加锁的过程, 这个很浪费资源, 很自然的我们想到了双重锁定的方法:
另外还有一种解决的思路, 就是我在类初始化的时候, 就把这个对象给创建出来, 以后每次调用的时候, 就不需要处理复杂的线程安全问题了。
稍微总结一下:
静态初始化的方式, 我们一般将他称为是饿汉式单例类, 他会提前占用系统资源, 而懒汉式单例类, ie, 在第一次被引用的时候,才调用自己的实例化, 会面临多线程安全的问题。需要根据需求选择不同的单例方式。
最后给出main.cpp
保证一个类只有一个实例, 并提供一个访问他的全局访问点
通常当我们需要确保一个类只有一个实例的时候, 应该选用单例模式, 让类自身负责保存他的唯一实例。这个类可以保证没有其他实例被创建, 同时提供一个访问这个唯一实例的一个方法。
一般我们可以使用 私有构造方法来实现这个功能, 让外界无法 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; }