首页 > 其他分享 >单例模式

单例模式

时间:2024-07-28 23:17:28浏览次数:13  
标签:std Singleton 模式 threads 单例 线程 include

目录

什么是单例模式?

单例模式属于简单设计模式的一种。在整个系统的生命周期内,单例类有且只有唯一一个对象,典型的应用比如日志的句柄。使用单例模式时需要考虑线程安全的问题,具体看后文具体的代码解析。

单例模式的特点

  • 单例类只能有一个实例。
  • 成员是私有的、静态的。
  • 禁止拷贝、赋值,构造函数、私有函数是私有的。

单例模式的实现方式

  • 懒汉模式:在需要使用的时候才实例化对象。
  • 饿汉模式:在系统刚启动时就实例化对象。

懒汉模式

实现一(非线程安全)

#include <iostream>
#include <array>
#include <thread>
#include <mutex>

#define MAX_THREAD_SIZE 10

class Singleton
{
public:
    static Singleton* GetInstance();
    static void DeleteInstance();
    void Print(int index);

    Singleton(const Singleton& kSingleton) = delete;
    Singleton* operator=(const Singleton& kSingleton) = delete;
private:
    Singleton();
    ~Singleton();

    static Singleton* singleton_;
};

Singleton::Singleton()
{
    std::cout << "构造函数" << std::endl;
}

Singleton::~Singleton()
{
    std::cout << "析构函数" << std::endl;
}

Singleton* Singleton::GetInstance()
{
    if (!singleton_)
    {
        singleton_ = new(std::nothrow) Singleton;
    }
    
    return singleton_;
}

void Singleton::DeleteInstance()
{
    if (singleton_)
    {
        delete singleton_;
        singleton_ = nullptr;
    }
}

void Singleton::Print(int index)
{
    std::cout << "线程" << index << ":" << this << std::endl;
}

Singleton* Singleton::singleton_ = nullptr;

int main()
{
    std::array<std::thread, MAX_THREAD_SIZE> threads;
   
    for (int i = 0; i < MAX_THREAD_SIZE; i++)
    {
        threads[i] = std::thread(&Singleton::Print, Singleton::GetInstance(), i);
        threads[i].join();
    }

    Singleton::DeleteInstance();

    return 0;
}

此种实现方式,可能会有两个线程同时进入GetInstance()函数,恰好同时判断出singleton_指针为空,各自new了一个Singleton对象,所以是非线程安全的,如果想要此种实现是线程安全的,那么对GetInstance()实现加上锁保护即可,详见实现二。

实现二(线程安全)

#include <iostream>
#include <array>
#include <thread>
#include <mutex>

#define MAX_THREAD_SIZE 10

class Singleton
{
public:
    static Singleton* GetInstance();
    static void DeleteInstance();
    void Print(int index);

    Singleton(const Singleton& kSingleton) = delete;
    Singleton* operator=(const Singleton& kSingleton) = delete;
private:
    Singleton();
    ~Singleton();

    static Singleton* singleton_;
    static std::mutex mutex_;
};

Singleton::Singleton()
{
    std::cout << "构造函数" << std::endl;
}

Singleton::~Singleton()
{
    std::cout << "析构函数" << std::endl;
}

Singleton* Singleton::GetInstance()
{
    if (!singleton_)
    {
        std::unique_lock<std::mutex> lock(mutex_);
        if (!singleton_)
        {
            singleton_ = new(std::nothrow) Singleton;
        }
    }
    
    return singleton_;
}

void Singleton::DeleteInstance()
{
    std::unique_lock<std::mutex> lock(mutex_);
    if (singleton_)
    {
        delete singleton_;
        singleton_ = nullptr;
    }
}

void Singleton::Print(int index)
{
    std::cout << "线程" << index << ":" << this << std::endl;
}

Singleton* Singleton::singleton_ = nullptr;
std::mutex Singleton::mutex_;

int main()
{
    std::array<std::thread, MAX_THREAD_SIZE> threads;
   
    for (int i = 0; i < MAX_THREAD_SIZE; i++)
    {
        threads[i] = std::thread(&Singleton::Print, Singleton::GetInstance(), i);
        threads[i].join();
    }

    Singleton::DeleteInstance();

    return 0;
}

通过在GetInstance()函数中添加锁的保护,可以保证有且只有一个线程进入并创建了Singleton类对象,从而保证了线程安全,但是多了锁的开销,那么有没有更好的方法呢?下面介绍C++11后最推荐的方式。

实现三(线程安全、推荐)

#include <iostream>
#include <array>
#include <thread>
#include <mutex>

#define MAX_THREAD_SIZE 10

class Singleton
{
public:
    static Singleton& GetInstance();
    void Print(int index);

    Singleton(const Singleton& kSingleton) = delete;
    Singleton& operator=(const Singleton& kSingleton)= delete;
private:
    Singleton();
    ~Singleton();
};

Singleton::Singleton()
{
    std::cout << "构造函数" << std::endl;
}

Singleton::~Singleton()
{
    std::cout << "析构函数" << std::endl;
}

Singleton& Singleton::GetInstance()
{
    static Singleton singleton_;
    return singleton_;
}

void Singleton::Print(int index)
{
    std::cout << "线程"  << index << ":" << this << std::endl;
}

int main()
{
    std::array<std::thread, MAX_THREAD_SIZE> threads;
   
    for (int i = 0; i < MAX_THREAD_SIZE; i++)
    {
        threads[i] = std::thread(&Singleton::Print, &Singleton::GetInstance(), i);
        threads[i].join();
    }

    return 0;
}

此种实现适用于C++11之后的程序,因为C++11规定:如果当变量在初始化的时候,并发同时进入声明语句,并发线程将会阻塞等待初始化结束。 这种返回局部静态变量的方式,更加的简洁高效,所以比较推荐。

饿汉模式

#include <iostream>
#include <array>
#include <thread>
#include <mutex>

#define MAX_THREAD_SIZE 10

class Singleton
{
public:
    static Singleton* GetInstance();
    static void DeleteInstance();
    void Print(int index);

    Singleton(const Singleton& kSingleton) = delete;
    Singleton& operator=(const Singleton& kSingleton) = delete;
private:
    Singleton();
    ~Singleton();

    static Singleton* singleton_;
};

Singleton::Singleton()
{
    std::cout << "构造函数" << std::endl;
}

Singleton::~Singleton()
{
    std::cout << "析构函数" << std::endl;
}

Singleton* Singleton::GetInstance()
{
    return singleton_;
}

void Singleton::DeleteInstance()
{
    if (singleton_)
    {
       delete singleton_;
       singleton_ = nullptr; 
    }
}

void Singleton::Print(int index)
{
    std::cout << "线程" << index << ":" << this << std::endl;
}

Singleton* Singleton::singleton_ = new(std::nothrow) Singleton;

int main()
{
    std::array<std::thread, MAX_THREAD_SIZE> threads;
   
    for (int i = 0; i < MAX_THREAD_SIZE; i++)
    {
        threads[i] = std::thread(&Singleton::Print, Singleton::GetInstance(), i);
        threads[i].join();
    }

    Singleton::DeleteInstance();

    return 0;
}

饿汉模式的实现,在程序启动时,就已经实例化了Singleton对象,因此,后续访问的都是同一个对象,是天然的线程安全的。

总结

单例模式是一种比较经典、常用的设计模式,面试也经常会问到,是一定要掌握的。如果在程序中需要创建一个唯一存在的实例对象,那么一定要考虑使用单例模式,优先使用懒汉模式中的返回局部静态变量的方法,切记保证线程安全。

标签:std,Singleton,模式,threads,单例,线程,include
From: https://www.cnblogs.com/Joe-zhu/p/18329136

相关文章

  • 设计模式实战:日志系统的设计与实现
    问题描述设计一个日志系统,支持在应用程序中记录日志信息。系统需要确保日志记录器是唯一的实例,支持不同的日志记录方法(如文件、数据库),并且能够适配不同的日志格式(如JSON、XML)。设计分析单例模式单例模式确保一个类只有一个实例,并提供一个全局访问点。日志系统中的日志记......
  • OAuth2 + Gateway统一认证一步步实现(公司项目能直接使用),密码模式&授权码模式
    文章目录认证的具体实现环境的搭建基础版授权服务搭建引入依赖创建数据表yml配置配置SpringSecurity定义认证授权的配置类授权服务器存储客户端信息修改授权服务配置,支持密码模式基础版授权服务测试授权码模式测试密码模式测试**测试校验token接口**整合JWT使用jwt基......
  • 设计模式:代理、装饰和适配器模式的区别
    结构对比讲实话,博主当初学习完整设计模式时,这三种设计模式单独摘哪一种都是十分清晰和明确的,但是随着模式种类的增加,在实际使用的时候竟然会出现恍惚,例如读开源代码时,遇到不以模式命名规范的代码时,一时难以说清具体是使用的这三种里的哪一种。之所以会出现混淆的原因是,三种模式......
  • springsecurity通过策略模式设置统一认证接口
     还是回到这张图:我们想不止使用数据库查找的方法去实现认证,而是使用一个统一的认证方法,首先需要修改DaoAuthenticationProvider内实现功能7的方法protectedvoidadditionalAuthenticationChecks(UserDetailsuserDetails,UsernamePasswordAuthenticationTokenauthenticatio......
  • chapter3------保护模式之从保护模式跳转回实模式
    1、准备合适的段选择子在保护模式下,段寄存器存储的是段选择子,而不是实际的段地址。要切换到实模式,需要准备一个适当的段选择子,它指向实模式下要访问的代码段。示例程序:LABEL_DESC_NORMAL:Descriptor0,0ffffh,DA_DRW;Normal描述符SelectorNormaleq......
  • PUMA:DOA估计模式的改进实现(Matlab代码实现)
     ......
  • Spring-MVC框架升级之单例模式及统一异常处理
    在SpringMVC框架的升级过程中,单例模式和统一异常处理是两个非常重要的方面。下面我将详细介绍这两个方面,并提供相应的示例代码和总结。1.单例模式(SingletonPattern)在Spring框架中,单例模式是默认的bean作用域。这意味着Spring容器中每个bean只有一个实例,这样可以......
  • 严格模式 模块化开发
    严格模式当你在脚本或函数的顶部添加"usestrict"语句时,你的代码将在严格模式下执行。这可以帮助你避免某些常见的编程陷阱,例如在不声明变量的情况下就使用它们,或者删除变量、函数或函数参数。在严格模式下,这样的操作都会抛出错误。1、使用严格模式后,变量必须使用var或者l......
  • chapter3------保护模式之初始化GDT
    实模式运行于16位的CPU环境下:16位的寄存器16位的数据总线20位的地址总线,以及1MB的寻址能力(2^20B)一个地址由段和偏移两部分组成,物理地址=段值x16+偏移(段值和偏移都是16位,段值左移四位最后计算出来的地址才是20位)保护模式运行与32位的CPU环境下32位的寄存器32位......
  • AP8660 DC-DC升压恒压芯片 PWM模式 内置24W 充电器方案
    产品描述AP8860一款宽电压范围降压型DC-DC电源管理芯片,内部集成功率MOS管、使能开关控制、基准电源、误差放大器、过热保护、限流保护、短路保护等功能,非常适合宽电压输入降压使用。AP8860带使能控制,可以大大节省外围器件,更加适合电池场合使用,具有很高的方案性价比。AP8660......