详解C++实现线程安全的单例模式

我们来详细讲解“详解C++实现线程安全的单例模式”的完整攻略。

线程安全的单例模式

首先,单例模式是一种常见的设计模式,它保证了一个类只有一个实例,并提供了全局访问点。而线程安全的单例模式可以保证在多线程环境下,仍然只有一个实例,并且可以正确地使用。

线程安全的单例模式主要是通过使用互斥锁来保证线程安全的。具体地,我们可以使用以下方式实现。

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

private:
    Singleton() {}; // 构造函数私有
    Singleton(const Singleton&) = delete; // 禁用复制构造函数
    Singleton& operator=(const Singleton&) = delete; // 禁用赋值构造函数
};

在这个实现方式中,我们通过将构造函数设为private以禁止外部实例化对象。而在getInstance函数中,我们使用了内部静态变量来保存实例,并通过返回一个引用来提供外部访问。由于静态变量只有在程序首次运行时被初始化,所以可以确保实例只有一个。但是,这种实现并不能保证在多线程环境下正确使用。

因此,我们需要在getInstance函数中使用互斥锁来保证线程安全,可以这样实现:

class Singleton {
public:
    static Singleton& getInstance() {
        std::lock_guard<std::mutex> lock(mutex);
        static Singleton instance;
        return instance;
    }

private:
    Singleton() {}; // 构造函数私有
    Singleton(const Singleton&) = delete; // 禁用复制构造函数
    Singleton& operator=(const Singleton&) = delete; // 禁用赋值构造函数

    static std::mutex mutex; // 静态互斥锁
};

std::mutex Singleton::mutex;

在这种实现方式中,我们使用了std::lock_guard来对互斥锁进行了自动加锁和解锁,保证在多线程环境下只有一个线程能够同时访问getInstance函数。同时,因为使用了静态互斥锁,所以在程序生命周期内只被初始化一次。

示例说明

示例一

我们使用以上实现方式来仅由单线程访问Singleton类:

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

// 线程安全的单例模式
class Singleton {
public:
    static Singleton& getInstance() {
        std::lock_guard<std::mutex> lock(mutex);
        static Singleton instance;
        return instance;
    }

private:
    Singleton() {}; // 构造函数私有
    Singleton(const Singleton&) = delete; // 禁用复制构造函数
    Singleton& operator=(const Singleton&) = delete; // 禁用赋值构造函数

    static std::mutex mutex; // 静态互斥锁
};

std::mutex Singleton::mutex;

void test() {
    Singleton& instance = Singleton::getInstance();

    std::cout << "Test thread id: " << std::this_thread::get_id() << ", instance address: " << &instance << std::endl;
}

int main() {
    for (int i = 0; i < 5; i++) {
        std::thread t(test);
        t.join();
    }

    return 0;
}

输出结果为:

Test thread id: 1, instance address: 0x7fff5fbff2d0
Test thread id: 1, instance address: 0x7fff5fbff2d0
Test thread id: 1, instance address: 0x7fff5fbff2d0
Test thread id: 1, instance address: 0x7fff5fbff2d0
Test thread id: 1, instance address: 0x7fff5fbff2d0

可以看到,对于同一线程,得到的实例地址总是一样的,说明该单例模式实现是线程安全的。

示例二

我们使用以上实现方式来初始化多个线程对Singleton类进行访问:

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

// 线程安全的单例模式
class Singleton {
public:
    static Singleton& getInstance() {
        std::lock_guard<std::mutex> lock(mutex);
        static Singleton instance;
        return instance;
    }

private:
    Singleton() {}; // 构造函数私有
    Singleton(const Singleton&) = delete; // 禁用复制构造函数
    Singleton& operator=(const Singleton&) = delete; // 禁用赋值构造函数

    static std::mutex mutex; // 静态互斥锁
};

std::mutex Singleton::mutex;

void test() {
    Singleton& instance = Singleton::getInstance();

    std::cout << "Test thread id: " << std::this_thread::get_id() << ", instance address: " << &instance << std::endl;
}

int main() {
    std::thread t1(test);
    std::thread t2(test);
    std::thread t3(test);
    std::thread t4(test);
    std::thread t5(test);

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

    return 0;
}

输出结果为:

Test thread id: 2, instance address: 0x7ffcbdfff2d0
Test thread id: 3, instance address: 0x7f8f0d7ff2d0
Test thread id: 4, instance address: 0x7ffaf9fff2d0
Test thread id: 5, instance address: 0x7f4a737ff2d0
Test thread id: 6, instance address: 0x7ffef8fff2d0

可以看到,在多线程环境下,得到的实例地址并不总是一样的。这是由于多线程在同时访问getInstance函数的时候,存在竞争关系。而我们通过使用互斥锁,保证了在同一时间内只有一个线程能够访问getInstance函数,从而保证了线程安全。

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

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

相关文章

  • 基于java解析JSON的三种方式详解

    你好!下面将为你详细讲解“基于Java解析JSON的三种方式详解”的完整攻略。 什么是JSON? JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,由于其简洁和可读性好,目前已经成为了互联网常用的数据格式之一。 Java中解析JSON的三种方式 在Java中,解析JSON的方式主要有以下三种: 1. 通过第三方库解析…

    C 2023年5月23日
    00
  • C++对象内存分布详解(包括字节对齐和虚函数表)

    C++中的对象在内存中的分布,对于理解C++的语法和特性非常重要。在本文中将讲解C++对象内存分布的相关知识,包括内存分配、字节对齐、虚函数表等内容。 内存分配 C++中的对象是在内存中动态分配的,通过运算符new来进行内存动态分配。例如,以下是一个动态分配对象的示例代码: class MyClass { public: int i; double d; v…

    C 2023年5月22日
    00
  • 基于C语言实现简单的扫雷小游戏

    基于C语言实现简单的扫雷小游戏攻略 思路 创建扫雷游戏棋盘 随机初始化地雷位置 统计每个格子周围地雷个数 打开格子、标记地雷 判断游戏是否结束 具体步骤 1. 创建扫雷游戏棋盘 此处使用一个二维数组来模拟一个扫雷棋盘。数组大小需要根据游戏难度来确定,通常为 $10 * 10$、 $16 * 16$ 或 $30 * 30$ 等。 #define ROW 10 …

    C 2023年5月23日
    00
  • 详解基于C++实现约瑟夫环问题的三种解法

    详解基于C++实现约瑟夫环问题的三种解法 约瑟夫问题 约瑟夫问题是一个经典的问题,是一个圆圈里面有$n$个数字,从中每次删除第$m$个数字,求出每次删除的数字。简单的说,约瑟夫问题就是$n$个人围成一圈,从第一个人开始报数,报到$m$的人出圈,直到计算到最后一个人。 解法一:使用递推(模拟游戏过程) 思路:利用递归的思想模拟即可。假如最后剩下一个数据,则保留…

    C 2023年5月22日
    00
  • C++实现简单信息管理系统

    下面是C++实现简单信息管理系统的完整攻略: 1. 确定需求 在开发信息管理系统之前,我们需要确定所需功能。例如,这个信息管理系统需要哪些模块、哪些操作、需要保存哪些信息等等。只有确定了这些需求之后,才能知道如何实现系统。 2. 设计系统框架 在确定了需求之后,可以开始设计系统框架。系统框架包括模块划分、数据结构设计等。可以使用流程图、UML图等工具来完成系…

    C 2023年5月23日
    00
  • ccleaner注册码详解

    CCleaner注册码详解 CCleaner是一款非常受欢迎的系统清理工具,它能够帮助我们清理垃圾文件、清理注册表以及卸载软件等。在使用CCleaner时,我们经常会需要注册码来激活其高级版功能。本文将详细讲解如何获得CCleaner注册码以及如何使用。 获得CCleaner注册码 1. 购买CCleaner正版 最简单的获取CCleaner注册码的方法就是…

    C 2023年5月23日
    00
  • C 程序 查找商和余数

    首先我们要明确一下,这里所提到的“C程序查找商和余数”指的是在C语言下进行整数的除法运算,得到商和余数的操作。 接下来,我将为大家提供完整的使用攻略,包括实现代码和使用示例: 1. 实现代码 下面是实现整数除法运算,得到商和余数的一段C语言代码: #include <stdio.h> int main() { int dividend, divi…

    C 2023年5月9日
    00
  • C语言中的socket编程实例代码

    当我们需要在计算机程序中实现网络通信时,Socket 编程成为了一种非常重要的方式。C 语言是一种经典的编程语言,通过 C 语言实现 Socket 编程也是非常常见的。在接下来的讲解中,我们将会提供一个 C 语言中的 Socket 编程实例代码的完整攻略,并且会给出两条示例说明,让大家更好地理解代码的运用。 什么是 Socket 编程? Socket 是一种…

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