从C++单例模式到线程安全详解

C++单例模式到线程安全详解

什么是单例模式

单例模式是一种设计模式,它允许一个类只创建一个实例,同时提供一个访问该实例的全局节点。这种模式常用于控制特定资源的访问,如数据库或者网络连接。

C++实现单例模式

在C++中,实现单例模式最常用的方法是使用静态成员变量和私有构造函数。具体实现步骤如下:
1. 将类的构造函数设置为私有。
2. 在类中定义一个静态私有成员变量,用于存储唯一实例。
3. 提供一个静态公有方法用于获取该唯一实例,如果唯一实例未创建,则先创建并返回该实例。

下面是一个简单的示例代码:

class Singleton {
public:
    static Singleton& getInstance() {
        static Singleton instance;
        return instance;
    }

private:
    Singleton() {} // 禁止外部调用构造函数
    ~Singleton() {}
    Singleton(const Singleton&) = delete; // 禁止拷贝构造函数
    Singleton& operator=(const Singleton&) = delete; // 禁止赋值构造函数
};

单例模式的线程安全问题

在单线程环境下,上述实现单例模式方法是可行的。然而,在多线程环境下,上述实现会存在线程安全问题。

例如,当有多个线程同时调用getInstance()方法时,会导致创建多个实例。这违反了单例模式的定义。

解决单例模式线程安全问题的方法

想要解决单例模式的线程安全问题,需要实现双重锁定。

具体实现步骤如下:
1. 将类的构造函数设置为私有。
2. 在类中定义一个静态私有成员变量,用于存储唯一实例。
3. 提供一个静态公有方法用于获取该唯一实例,并使用双重锁定确保线程安全。

下面是一个示例代码:

#include <mutex>

class Singleton {
public:
    static Singleton& getInstance() {
        if (instance_ == nullptr) {
            std::lock_guard<std::mutex> lock(mutex_);
            if (instance_ == nullptr) {
                instance_ = new Singleton();
            }
        }
        return *instance_;
    }

private:
    Singleton() {} // 禁止外部调用构造函数
    ~Singleton() {}
    Singleton(const Singleton&) = delete; // 禁止拷贝构造函数
    Singleton& operator=(const Singleton&) = delete; // 禁止赋值构造函数

    static Singleton* instance_; // 静态成员变量
    static std::mutex mutex_; // 互斥锁
};

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

在这个示例代码中,我们使用了std::mutex类型的互斥锁来保证线程安全。当getInstance()方法被第一个线程调用时,会获取到互斥锁,接着检查实例是否为空。如果为空,则创建一个新的实例,并将其赋值给instance_。最后,返回该实例。

示例说明

下面提供两个示例说明。

示例1:多线程输出单例模式的唯一实例

下面是一个多线程输出单例模式的唯一实例的示例代码:

#include <iostream>
#include <thread>

void printInstance(int tid) {
    auto& instance = Singleton::getInstance();
    std::cout << "Thread ID: " << tid << " Singleton instance address: " << &instance << std::endl;
}

int main() {
    std::thread t1(printInstance, 1);
    std::thread t2(printInstance, 2);
    std::thread t3(printInstance, 3);

    t1.join();
    t2.join();
    t3.join();

    return 0;
}

在这个示例代码中,我们定义了一个输出单例模式唯一实例地址的函数printInstance(),并分别启动了3个线程来调用该函数。通过查看输出结果,可以发现3个线程输出的唯一实例地址相同,表明单例模式的确实现了实例的唯一性。

示例2:单例模式的线程安全问题

下面是一个多线程创建单例实例的示例代码:

#include <iostream>
#include <thread>

void createInstance(int tid) {
    // 由于没有双重锁定,线程会创建多个实例
    auto& instance = Singleton::getInstance();
    std::cout << "Thread ID: " << tid << " Singleton instance address: " << &instance << std::endl;
}

int main() {
    std::thread t1(createInstance, 1);
    std::thread t2(createInstance, 2);
    std::thread t3(createInstance, 3);

    t1.join();
    t2.join();
    t3.join();

    return 0;
}

在这个示例代码中,我们定义了一个创建单例实例的函数createInstance(),并分别启动了3个线程来调用该函数。通过查看输出结果,可以发现3个线程创建的实例地址不同,表明单例模式存在线程安全问题。

总结

单例模式是一种常用的设计模式,可以保证程序中某些资源的唯一性。在多线程环境中,为了解决单例模式的线程安全问题,需要使用双重锁定的方法。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:从C++单例模式到线程安全详解 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • C++堆栈类模板实现代码

    C++中的堆栈类是一种常用的数据结构,可以实现后进先出(LIFO)的数据存储和处理方式。 下面是一个C++堆栈类模板的实现代码攻略,主要包括以下几个方面: 堆栈类模板的定义和实现 堆栈类模板由两个部分组成:头文件(.h文件)和源文件(.cpp文件)。 头文件中需要包含以下内容: 头文件保护宏定义,避免重复引用。 类定义,定义堆栈类模板及其成员函数。 类成员,…

    C 2023年5月24日
    00
  • C 程序 查找字符的 ASCII 值

    为了查找字符的ASCII值,我们可以使用C程序来完成。下面是使用攻略: 准备工作 在开始使用C语言编写程序之前,需要先安装一些开发环境,包括GCC编译器,以及一个代码编辑器,比如Visual Studio Code等。 步骤如下: 输入需要查找ASCII值的字符 首先,我们需要通过键盘输入需要查找ASCII值的字符,使用C语言中的字符变量来存储输入的字符。比…

    C 2023年5月9日
    00
  • 前端常见跨域解决方案(全)

    前端常见跨域解决方案,主要是因为浏览器同源策略(Same Origin Policy)的限制,导致一个域名下的前端代码无法直接请求另一个域名的资源,这就是所谓的“跨域”。 下面介绍几种前端常见的跨域解决方案。 1. JSONP JSONP 是前端跨域解决方案中最简单、最常用的一种。它通过动态创建 <script> 标签,再请求一个带有回调函数的接…

    C 2023年5月23日
    00
  • C语言实现贪吃蛇游戏设计

    C语言实现贪吃蛇游戏设计攻略 简介 贪吃蛇游戏是一款非常经典的小游戏,它在很多平台上都有实现,如PC、移动设备等。本攻略的目的是介绍如何使用C语言实现贪吃蛇游戏。 设计思路 初始化游戏 绘制界面 进行游戏循环 获取用户输入 移动蛇 判断蛇是否吃到食物 生成新的食物 判断游戏是否结束 游戏结束,清理资源 代码实现 初始化游戏 在开始游戏前,需要初始化游戏所需要…

    C 2023年5月23日
    00
  • C语言如何改变字体颜色

    下面是C语言如何改变字体颜色的完整攻略。在Windows命令行界面中,我们可以用如下的C语言代码来修改字体颜色: #include <Windows.h> int main() { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleTextAttribute(hConsole…

    C 2023年5月23日
    00
  • Golang哈希算法实现配置文件的监控功能详解

    Golang哈希算法实现配置文件的监控功能详解 介绍 在开发中,经常需要读取配置文件来动态调整运行时参数。为了及时更新配置文件的修改,我们需要实现一个能够监控配置文件变化并自动加载的功能。本文介绍使用 Golang 哈希算法实现配置文件监控的方法。 哈希算法介绍 哈希算法是一种将任意长的消息压缩到某一固定长度的消息摘要的函数。摘要的意义在于保证数据的完整性,…

    C 2023年5月23日
    00
  • C语言实现简易通讯录完整流程

    C语言实现简易通讯录完整流程 1. 需求分析 在编写“C语言实现简易通讯录”的过程中,我们需要实现以下功能:1. 添加联系人2. 删除联系人3. 修改联系人4. 查找联系人5. 显示联系人 2. 思路设计 2.1 数据结构设计 为了实现通讯录功能,我们可以定义如下结构体存储联系人信息: #define NAME_MAX_LENGTH 20 #define P…

    C 2023年5月23日
    00
  • C程序 使用递归查找自然数之和

    C程序使用递归查找自然数之和 概述 递归是一种函数自我调用的方式,通过递归可以简洁地解决一些复杂的问题。在C语言中,可以使用递归实现查找自然数之和的功能,本文将详细介绍该功能的实现方法及使用攻略。 实现方法 使用递归计算自然数之和,需要使用到如下几个步骤: 判断递归终止的条件,通常是n变为0或1时返回相应的值。 使用函数自身进行递归调用,将n-1作为参数传入…

    C 2023年5月9日
    00
合作推广
合作推广
分享本页
返回顶部