欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 金融 > 设计模式——单例模式

设计模式——单例模式

2024/10/24 15:14:18 来源:https://blog.csdn.net/weixin_42903300/article/details/139780199  浏览:    关键词:设计模式——单例模式

单例模式(Singleton)

单例模式是一种创建型设计模式, 保证一个类只有一个实例,并提供一个访问它的全局访问点

单例模式解决了两个问题:

  1. 保证一个类只有一个实例。控制类的实例数量的常见原因是控制某些共享资源(例如数据库或文件)的访问权限。
  2. 为该实例提供一个全局访问节点
    但是单例模式违反了单一职责原则

在这里插入图片描述

单例模式的实现方式

  1. 在类中添加一个私有静态成员变量用于保存单例实例
  2. 声明一个共有静态构造方法用于获取单例实例
  3. 在静态方法中实现"延迟初始化"。 该方法会在首次被调用时创建一个新对象, 并将其存储在静态成员变量中。 此后该方法每次被调用时都返回该实例。
  4. 将类的构造函数设为私有。类的静态方法仍能调用构造函数,但是其他对象不能调用
  5. 检查客户端代码,将对单例的构造函数的调用替换为对其静态构建方法的调用。
#include <iostream>
#include <string>using namespace std;class Singleton
{
public:// 本类实例的唯一全局访问点static Singleton &GetInstance(){// 如果实例不存在,创建实例if (instance == nullptr){instance = new Singleton();cout << "首次创建实例" << endl;}else{cout << "重复创建,返回同一个实例" << endl;}return *instance;}~Singleton(){delete instance;instance = nullptr;}private:// 私有的静态指针,指向单例static Singleton *instance;// 私有的构造函数,防止外部代码创建类的实例Singleton() {}
};// 静态成员需要在类外定义
Singleton *Singleton::instance = nullptr;// 客户端代码
static void Client()
{Singleton &s1 = Singleton::GetInstance();Singleton &s2 = Singleton::GetInstance();Singleton &s3 = Singleton::GetInstance();
}int main()
{Client();return 0;
}

输出为

首次创建实例
重复创建,返回同一个实例
重复创建,返回同一个实例

多线程时候的单例模式

如果main函数改写成

int main()
{// 线程容器vector<thread> threads;// 创建100个线程for (int i = 0; i < 10; i++){threads.emplace_back(Client);}// 等待所有线程完成for(auto& th:threads){th.join();}return 0;
}

注意将s2和s3注释掉了
输出变为

重复创建,返回同一个实例首次创建实例重复创建,返回同一个实例
首次创建实例
首次创建实例重复创建,返回同一个实例
重复创建,返回同一个实例
重复创建,返回同一个实例重复创建,返回同一个实例重复创建,返回同一个实例

出现了线程安全问题,有多个线程首次创建了实例
原因是GetInstance方法中条件判断(if (instance == nullptr))和实例创建(instance = new Singleton();)之间的代码片段并不是原子操作,可能会有多个线程同时进入这个条件判断,并且创建多个实例。

加锁的版本

class Singleton
{
public:// 本类实例的唯一全局访问点static Singleton &GetInstance(){// 加锁lock_guard<mutex> lock(mutex_);// 如果实例不存在,创建实例if (instance == nullptr){instance = new Singleton();cout << "首次创建实例" << endl;}else{cout << "重复创建,返回同一个实例" << endl;}return *instance;}~Singleton(){delete instance;instance = nullptr;}private:// 私有的静态指针,指向单例static Singleton *instance;// 私有的构造函数,防止外部代码创建类的实例Singleton() {}// 锁static mutex mutex_;
};// 静态成员需要在类外定义
Singleton *Singleton::instance = nullptr;
mutex Singleton::mutex_;

加锁解决了线程安全的问题,但是新的问题来了,不管三七二十一都加锁,这样很影响性能

双重检查锁

#include <iostream>
#include <string>
#include <thread>
#include <vector>
#include <mutex>using namespace std;class Singleton
{
public:// 本类实例的唯一全局访问点static Singleton &GetInstance(){// 如果实例不存在,创建实例if (instance == nullptr){// 加锁lock_guard<mutex> lock(mutex_);// 第二次检查if (instance == nullptr){instance = new Singleton();cout << "首次创建实例" << endl;}}else{cout << "重复创建,返回同一个实例" << endl;}return *instance;}~Singleton(){delete instance;instance = nullptr;}private:// 私有的静态指针,指向单例static Singleton *instance;// 私有的构造函数,防止外部代码创建类的实例Singleton() {}// 锁static mutex mutex_;
};// 静态成员需要在类外定义
Singleton *Singleton::instance = nullptr;
mutex Singleton::mutex_;// 客户端代码
static void Client()
{Singleton &s1 = Singleton::GetInstance();// Singleton &s2 = Singleton::GetInstance();// Singleton &s3 = Singleton::GetInstance();
}int main()
{// 线程容器vector<thread> threads;// 创建100个线程for (int i = 0; i < 10; i++){threads.emplace_back(Client);}// 等待所有线程完成for(auto& th:threads){th.join();}return 0;
}

饿汉式

上面的代码是单例模式的懒汉式。所谓懒汉式,就是在程序需要用到这个类的实例的时候采取创建,所以在多线程的场景下存在线程安全问题。

饿汉式,是指在程序在首次加载类的时候就会实例化,所以不存在线程安全问题。

代码如下

// 饿汉式实现
#include <iostream>  
#include <string>  
#include <thread>  
#include <vector>  using namespace std;  class Singleton  
{  
public:  // 本类实例的唯一全局访问点  static Singleton &GetInstance()  {  // 直接返回已经初始化的实例  return instance;  }  // 禁止拷贝构造函数和赋值操作符  Singleton(const Singleton&) = delete;  Singleton& operator=(const Singleton&) = delete;  private:  // 私有的静态实例,在类声明时初始化  static Singleton instance;  // 私有的构造函数,防止外部代码创建类的实例  Singleton()  {  cout << "首次创建实例" << endl;  }  
};  // 静态成员需要在类外定义  
Singleton Singleton::instance;  // 客户端代码  
static void Client()  
{  Singleton &s = Singleton::GetInstance();  
}  int main()  
{  // 线程容器  vector<thread> threads;  // 创建10个线程  for (int i = 0; i < 10; i++)  {  threads.emplace_back(Client);  }  // 等待所有线程完成  for(auto& th:threads)  {  th.join();  }  return 0;  
}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com