• C++设计模式-创建型设计模式:单例模式


    目录

    单例模式简介

    单例模式的典型疑问与优缺点:饿汉、懒汉与多线程安全

    单例模式的扩展与应用-缓存


    单例模式简介

    每次程序运行都要读config.ini,由生成的对象传入参数,但计算机读取IO缓慢,而且这些配置基本不会变化,如果每次运行程序都要重新new这个对象,会带来很多不必要的损耗。

    那么就需要整个程序运行中有且只有一个全局的对象,这个需求点就是单例模式。

    1. #include
    2. #include
    3. using namespace std;
    4. //外部程序没有机会new这个class
    5. class Singleton {
    6. private:
    7. Singleton() {
    8. m_singer = nullptr;
    9. cout << "构造一个SIngleton" << endl;
    10. }
    11. public:
    12. static Singleton* getInstance() {
    13. if (m_singer == nullptr) {
    14. m_singer = new Singleton;
    15. }
    16. return m_singer;
    17. }
    18. private:
    19. static Singleton* m_singer;
    20. };
    21. Singleton* Singleton::m_singer = nullptr;//懒汉式:延迟加载
    22. //Singleton* Singleton::m_singer = new Singleton;//饿汉式
    23. //C++中构造函数是线程不安全的
    24. int main() {
    25. Singleton* p1 = Singleton::getInstance();
    26. Singleton* p2 = Singleton::getInstance();
    27. printf("%x,%x", p1, p2);
    28. return 0;
    29. }

     

    单例模式的典型疑问与优缺点:饿汉、懒汉与多线程安全

    上面的例子就是懒汉式单例。

    但是如果这个单例是多线程,会出现什么问题?

    假设线程1在运行到m_singer=null时时间片轮转完了,那么此时就会切换到线程2中。

    此时线程2也会进入到m_singer=null 这个if语句块中,并创建一个Singleton。

    此时线程2结束,切回线程1,线程1继续执行刚才的现场,又创建了一个Singleton!

    那么如何解决这个问题,根据操作系统的知识,我们需要为互斥执行的语句创建临界区,并进行互斥访问。

    饿汉式与懒汉式的区别在于,懒汉式是一种延迟加载的方式,刚开始赋值为null,直到加载的时候才创建对象;而饿汉式在一开始初始化的时候就创建了对象,分配了资源。

    因为计算机进行I/O操作十分缓慢,因此延迟加载是一种提升性能的手段。有助于资源的合理使用。

     

    单例模式的扩展与应用-缓存

    • 既然我们可以控制全局生成一个对象,那么有没有需要生成一个以上的对象呢?
    • 缓存设计与Singleton的扩展

    集群。一台机器再强大,也不一定能抗住所有人的访问,所以可以用多台机器搭建一个集群。

    1. //回避多线程的安全问题
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. class Singleton;
    7. static std::mapmyMap = std::map();
    8. //懒汉,延迟加载
    9. class Singleton {
    10. private:
    11. Singleton() {
    12. m_singer = nullptr;
    13. cout << "单例正在创建" << endl;
    14. }
    15. public:
    16. static Singleton* getInstance() {
    17. //std::map::iterator it = myMap.find(DEFAULT_KEY);
    18. if (myMap.find(DEFAULT_KEY) != myMap.end()) {
    19. return myMap.find(DEFAULT_KEY)->second;
    20. }
    21. if (m_singer == nullptr) {
    22. m_singer = new Singleton;
    23. myMap[DEFAULT_KEY] = m_singer;
    24. }
    25. return m_singer;
    26. }
    27. private:
    28. static Singleton* m_singer;
    29. static string DEFAULT_KEY;
    30. };
    31. Singleton* Singleton::m_singer = nullptr;
    32. string Singleton::DEFAULT_KEY = "one";
    33. int main() {
    34. Singleton* p1 = Singleton::getInstance();
    35. Singleton* p2 = Singleton::getInstance();
    36. printf("p1=%x, p2=%x\n", p1, p2);
    37. return 0;
    38. }

     

     

    如果我们对这个DEFAULT_KEY做一些事情,让它成为多例。

    1. #include
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. //缓存实例个数
    7. const static int NUM_MAX = 5;
    8. class Singleton;
    9. static std::map<int, Singleton*>myMap = std::map<int, Singleton*>();
    10. //懒汉,延迟加载
    11. class Singleton {
    12. private:
    13. Singleton() {
    14. m_singer = nullptr;
    15. cout << "单例正在创建" << endl;
    16. }
    17. public:
    18. static Singleton* getInstance() {
    19. m_singer = myMap[m_InstanceCount];
    20. if (m_singer == nullptr) {
    21. m_singer = new Singleton;
    22. myMap[m_InstanceCount] = m_singer;
    23. }
    24. m_InstanceCount++;
    25. if (m_InstanceCount > NUM_MAX) {
    26. m_InstanceCount = 1;
    27. }
    28. return m_singer;
    29. }
    30. private:
    31. static Singleton* m_singer;
    32. static int m_InstanceCount;//存放实例个数
    33. };
    34. Singleton* Singleton::m_singer = nullptr;
    35. int Singleton::m_InstanceCount = 1;
    36. int main() {
    37. Singleton* p1 = Singleton::getInstance();
    38. Singleton* p2 = Singleton::getInstance();
    39. Singleton* p3 = Singleton::getInstance();
    40. Singleton* p4 = Singleton::getInstance();
    41. Singleton* p5 = Singleton::getInstance();
    42. printf("p1=%x, \np2=%x, \np3=%x, \np4=%x, \np5=%x, \n\n", p1, p2, p3, p4, p5);
    43. Singleton* p6 = Singleton::getInstance();
    44. Singleton* p7 = Singleton::getInstance();
    45. Singleton* p8 = Singleton::getInstance();
    46. Singleton* p9 = Singleton::getInstance();
    47. Singleton* p10 = Singleton::getInstance();
    48. printf("p6=%x, \np7=%x, \np8=%x, \np9=%x, \np10=%x, ", p6, p7, p8, p9, p10);
    49. return 0;
    50. }

    代码中写了10个instance,但是实际上只有5个instance被创建。 

    在实际应用中,可能集群需要维护5个单例,以便多用户访问和使用。

     

  • 相关阅读:
    pytorch 手写数字识别例子
    C语言每日一题(19)回文素数
    Ubuntu:编译升级Linux内核
    人人都写过的5个Bug!
    spring MVC源码探索之AbstractHandlerMethodMapping
    Excel练习:双层图表
    shell 脚本变量
    STM32学习笔记:驱动SPI外设读写FLASH
    【精通内核】Linux 内核写锁实现原理与源码解析
    《使用Gin框架构建分布式应用》阅读笔记:p52-p76
  • 原文地址:https://blog.csdn.net/Jason6620/article/details/126096299