C++单例模式

局部静态变量方式

cpp 复制代码
//通过静态成员变量实现单例
//懒汉式
class Single2
{
private:
    Single2()
    {
    }
    Single2(const Single2 &) = delete;
    Single2 &operator=(const Single2 &) = delete;
public:
    static Single2 &GetInst()
    {
        static Single2 single;
        return single;
    }
};

一个经典的懒汉式单例模式实现,通过静态成员变量来确保 Single2 类只有一个实例。这个实现是正确的,但有几点需要注意:

  1. 线程安全性 :在C++11及更高版本中,局部静态变量(如 GetInst 方法中的 static Single2 single)的初始化是线程安全的。因此,这个实现是线程安全的,不会在多线程环境中引发问题。

  2. 构造函数和赋值运算符的删除 :你通过 delete 删除了拷贝构造函数和赋值运算符,这可以防止在外部创建类的拷贝或通过赋值创建多个实例,这是单例模式的正确做法。

  3. 懒汉式单例的使用 :懒汉式单例在首次调用 GetInst 时才会创建实例,这样可以避免程序启动时不必要的内存消耗。

在C++11以下的版本中,使用你提供的懒汉式单例实现可能会遇到线程安全问题。具体来说,问题出现在静态局部变量 single 的初始化上。

线程安全问题

在C++11之前,局部静态变量的初始化不是线程安全的。如果多个线程同时首次调用 GetInst 方法,有可能会发生竞态条件(race condition),导致创建出多个 Single2 实例,破坏了单例模式的初衷。

解决方法

如果需要在C++11以下的版本中确保线程安全,可以考虑以下几种方法:

  1. 双重检查锁定(Double-Checked Locking):
cpp 复制代码
class Single2
{
private:
    static Single2* instance;
    static std::mutex mtx;
    Single2() {}
    Single2(const Single2 &) = delete;
    Single2 &operator=(const Single2 &) = delete;

public:
    static Single2* GetInst()
    {
        if (instance == nullptr) // 第一次检查
        {
            std::lock_guard<std::mutex> lock(mtx); // 加锁
            if (instance == nullptr) // 第二次检查
            {
                instance = new Single2();
            }
        }
        return instance;
    }
};

Single2* Single2::instance = nullptr;
std::mutex Single2::mtx;
  1. 静态成员变量初始化(线程不安全版本):

如果你确定单例只会在单线程环境下使用,可以使用以下更简单的方式:

cpp 复制代码
class Single2
{
private:
    static Single2* instance;
    Single2() {}
    Single2(const Single2 &) = delete;
    Single2 &operator=(const Single2 &) = delete;

public:
    static Single2* GetInst()
    {
        if (instance == nullptr)
        {
            instance = new Single2();
        }
        return instance;
    }
};

Single2* Single2::instance = nullptr;

这种实现不适用于多线程环境。

C++11 引入了一些新的特性和改进,其中之一就是保证了局部静态变量的线程安全性。这意味着在 C++11 及更高版本中,局部静态变量的初始化是由编译器自动处理的,并且保证了这种初始化过程在多线程环境下是安全的。

C++11 之前的问题

在 C++11 之前,当多个线程同时首次访问一个局部静态变量时,可能会出现竞态条件(Race Condition),导致多个线程同时进入变量的初始化过程。由于这个初始化过程没有得到线程的同步保护,可能会导致初始化多次,进而破坏单例模式的唯一性要求。

C++11 的改进

C++11 标准引入了以下保证:

  • 局部静态变量的初始化是线程安全的:C++11 规定,如果多个线程试图同时初始化同一个局部静态变量,只有一个线程会成功初始化它,而其他线程会被阻塞,直到初始化完成。这就确保了静态局部变量在多线程环境下也只会被初始化一次。

  • 懒汉式单例的线程安全性:由于局部静态变量的初始化已经是线程安全的,在 C++11 及更高版本中,可以安全地使用局部静态变量来实现懒汉式单例模式,无需额外的锁机制来防止竞态条件。

C++11 线程安全的实现机制

在底层,C++11 通过引入新的机制,确保了局部静态变量初始化的线程安全。这些机制包括:

  1. 线程同步机制:C++11 在初始化局部静态变量时,使用了编译器提供的同步机制(如锁、屏障等)来确保在多线程环境下,只有一个线程可以执行初始化代码。

  2. 按需初始化:C++11 确保局部静态变量在首次使用时才会被初始化,这种方式不仅节省资源,还保证了多线程环境中的唯一性。

相关推荐
2603_9651481110 分钟前
电商选品调研:通过关键词搜索API获取市场热销商品,结合商品详情API分析竞品卖点与定价
开发语言·数据库·人工智能·python·信息可视化
土豆.exe27 分钟前
nf_tables 的 verdict 被读了两次:一个十年未闭合的位域错配
java·数据库·网络安全·缓存
xixiaoyunya1 小时前
Python 异步编程入门:用 asyncio 轻松实现并发任务
开发语言·python·php
学逆向的1 小时前
C 循环结构的执行效率分析
c语言·开发语言·数据结构·网络安全
小坏讲微服务2 小时前
刚发布一个月的JDK 27,为什么我建议你立刻升级?
java·开发语言·spring boot·spring·架构·求职招聘·springboot4
kunicoding2 小时前
多线程编程核心概念详解:从线程基础到并发安全
java·jvm·安全
snow@li2 小时前
SpringBoot:核心注解全景梳理 / @xxx 叫“注解”(Annotation)
java·spring
devilnumber2 小时前
避坑_IDEA 提取方法(Extract Method)工具
java·ide·intellij-idea
鬼手点金2 小时前
Agent 的学习机制:后训练、上下文学习与外部化学习
java·人工智能·git·学习·自动化·nanogpt
小陈不好吃3 小时前
equals 与 hashCode 的契约关系:从 HashMap 到 Lombok 注解
java·hashmap·lombok