单例类:

(1) 单例类保证全局只有一个唯一的实例对象。

(2) 单例类保证只有唯一的接口获取这唯一实例。

非线程安全的单例类举例:

 class CSingleton
{
public:
~CSingleton(){}
static CSingleton * getInstance()
{
if (m_instance == nullptr)
{
m_instance = new CSingleton;
}
return m_instance;
}
static void delInstance()
{
if (m_instance)
{
delete m_instance;
m_instance = nullptr;
}
}
void print()
{
std::cout << "print test" << std::endl;
}
private:
CSingleton(){}
CSingleton & operator=(const CSingleton & ) = delete;
CSingleton(const CSingleton &) = delete;
private:
static CSingleton * m_instance;
}; CSingleton * CSingleton::m_instance = nullptr;

上述单例类面对多线程并发访问时会出错。

看如下线程安全的单例类(非C++11实现)

 class CSingleton
{
public:
~CSingleton() {}
static CSingleton * getInstance()
{
if (m_instance == nullptr)
{
std::lock_guard<std::mutex> lgd(m_mt);
if (m_instance == nullptr)
{
m_instance = new CSingleton;
}
}
return m_instance;
}
static void delInstance()
{
std::lock_guard<std::mutex> lgd(m_mt);
if (m_instance)
{
delete m_instance;
m_instance = nullptr;
}
}
void print()
{
std::cout << "print test" << std::endl;
}
private:
CSingleton() {}
CSingleton & operator=(const CSingleton & ) = delete;
CSingleton(const CSingleton &) = delete;
private:
static CSingleton * m_instance;
static std::mutex m_mt;
}; CSingleton * CSingleton::m_instance = nullptr;
std::mutex CSingleton::m_mt;

当然绝对的线程安全还是有问题,因为C++创建对象时,会执行1、分配内存,2 调用构造,3 赋值操作三步操作,然而现代CPU和编译器高并发下可能

会进行乱序重排操作,因而创建对象new CSingleton的第2步可能会晚于第3步进行指令调用,因而导致出现未定义的的行为。

举例:

线程A : getInstance 判断 instance是否为空,为空则

线程A : 分配内存  此时CPU乱序指令重排,赋值操作提前

线程B : getInsnace 判断instance是否为空,非空,则返回

线程B : 使用了未初始化的instacne 出现未定义行为。

线程A : 调用构造函数对instance初始化。

因此要解决上述问题需要引入内存栅栏来确保指令运行的同步性。在CPU指令重排的前提下保持数据的一致性。

C++11支持线程安全的单例类:

C++11的单例模式的实现

 class CSingleton
{
public:
~CSingleton() {}
static CSingleton & getInstance()
{
static CSingleton m_instance;
return m_instance;
}
void print()
{
std::cout << "print test" << std::endl;
}
};

返回静态局部对象的引用,C++11中是线程安全的。

验证一下:

 class CStatic
{
public:
CStatic()
{
std::cout << "construct begin" << std::endl;
Sleep();
std::cout << "construct end" << std::endl;
}
void print()
{
std::cout << "print" << std::endl;
std::cout << s_num++ << std::endl;
}
static int s_num;
static std::mutex s_mt;
};
 int CStatic::s_num = ;
std::mutex CStatic::s_mt; //
void thread_func()
{
static CStatic st;
st.print();
} int main()
{
std::vector<std::thread> vecThread;
for (auto i = ; i< ; i++)
{
vecThread.push_back(std::thread(thread_func));
}
for (auto i = ; i< ; i++)
{
vecThread[i].join();
}
//
system("pause");
return ;
}

首先我们创建一个CStatic类,然后创建8个线程来启动thread_func(),thread_func()初始化了一个静态CStatic对象,(静态局部变量仅被初始化一次)

然后接着运行。我们发现,当首个线程初始化CStatic时,其他线程都是被阻塞的,从构造函数的begin和end中可以看到,我们故意让其停留5s,

如下图,其他线程都是在st被初始化之后才运行。

所以CStatic静态局部对象被构造的过程中是线程安全的,但是其拥有的成员变量则不是线程安全的。

因此我们增加个简单的锁,

 class CStatic
{
public:
CStatic()
{
std::cout << "construct begin" << std::endl;
Sleep();
std::cout << "construct end" << std::endl;
}
void print()
{
std::lock_guard<std::mutex> lgd(s_mt);
std::cout << "print" << std::endl;
std::cout << s_num++ << std::endl;
}
static int s_num;
static std::mutex s_mt;
};

 

C++11 单例类实现的更多相关文章

  1. c++:自己动手实现线程安全的c++单例类

    前段时间使用c++做项目开发,需要根据根据配置文件路径加载全局配置文件,并对外提供唯一访问点.面对这样一个需求,自然的就想到了使用单例模式来创建一个单例配置对象,供外部调用.一开始想使用boost中自 ...

  2. 游戏设计模式——C++单例类

    前言: 本文将探讨单例类设计模式,单例类的懒汉模式/饿汉模式,单例类的多线程安全性,最后将利用C++模板减少单例类代码量. 本文假设有一个Manager管理类,并以此为探究单例类的设计模式. 懒汉模式 ...

  3. java单例类/

    java单例类  一个类只能创建一个实例,那么这个类就是一个单例类 可以重写toString方法 输出想要输出的内容 可以重写equcal来比较想要比较的内容是否相等 对于final修饰的成员变量 一 ...

  4. iOS中编写单例类的心得

    单例 1.认识过的单例类有哪些: NSUserDefaults.NSNotificationCenter.NSFileManager.UIApplication 2.单例类 单例类某个类在代码编写时使 ...

  5. 如何防止JAVA反射对单例类的攻击?

    在我的上篇随笔中,我们知道了创建单例类有以下几种方式: (1).饿汉式; (2).懒汉式(.加同步锁的懒汉式.加双重校验锁的懒汉式.防止指令重排优化的懒汉式); (3).登记式单例模式; (4).静态 ...

  6. 0013 Java学习笔记-面向对象-static、静态变量、静态方法、静态块、单例类

    static可以修饰哪些成员 成员变量---可以修饰 构造方法---不可以 方法---可以修饰 初始化块---可以修饰 内部类(包括接口.枚举)---可以修饰 总的来说:静态成员不能访问非静态成员 静 ...

  7. 设计模式(java) 单例模式 单例类

    ·单例类 单实例类,就是这个类只能创建一个对象,保证了对象实例的唯一性. 1.单例模式( Singleton Pattern) 是一个比较简单的模式, 其定义如下:Ensure a class has ...

  8. [转]单例模式——C++实现自动释放单例类的实例

    [转]单例模式——C++实现自动释放单例类的实例 http://www.cnblogs.com/wxxweb/archive/2011/04/15/2017088.html http://blog.s ...

  9. iOS - OC SingleClass 单例类

    前言 单例对象能够被整个程序所操作.对于一个单例类,无论初始化单例对象多少次,也只能有一个单例对象存在,并且该对象是全局的,能够被整个系统访问到. 特点: 在内存中只有一个实例 提供一个全局的访问点 ...

随机推荐

  1. Github+yeoman+gulp-angular初始化搭建angularjs前端项目框架

    在上篇文章里面我们说到了Github账号的申请与配置 那么当你有了Github账号并创建了一个自己的Github项目之后,首要的当然是搭建自己的项目框架啦! 本人对自己的定位是web前端狗,常用开发框 ...

  2. maven命令更新子父项目的pom版本

    Q:一个maven项目,有多个子module,在顶级父pom.xml里设置<version>1.0.0-SHAPSHOT</version>,在子pom.xml里都用了 < ...

  3. 使用openXML 不用插件导出excel

    注释很详细,不做解释了,有疑问可以提问 using System.IO; using System.Text; namespace iLIS.Common { /// <summary> ...

  4. 学习笔记:javascript内置对象:字符串对象

    1.字符串的创建   var str = "Hello Microsoft!";   2.字符串属性   constructor  返回创建字符串属性的函数   length    ...

  5. JAVA内存模型5-锁

    锁的释放-获取建立的happens before关系        锁是java并发编程中最重要的同步机制.锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息.下面是锁释放-获 ...

  6. iOS开发之判断用户是否打开APP通知开关

    一.前言 在多数移动应用中任何时候都只能有一个应用程序处于活跃状态,如果其他应用此刻发生了一些用户感兴趣的那么通过通知机制就可以告诉用户此时发生的事情.iOS中通知机制又叫消息机制,其包括两类:一类是 ...

  7. 关于Dubbo一个接口多个实现的解决方案

    如题,其实这个问题在官方文档中已经说明了.我直接贴图就好了 更多学习请参考:minglisoft.cn/technology

  8. linux统计多个文件大小总和

    首先:查看当前文件夹大小的命令是: [root@host1 test]# du -sh 39M . 查看当前文件夹下所有文件的大小: [root@host1 test]# du -sh * 108K ...

  9. 《Android进阶》之第三篇 深入理解android的消息处理机制

    Android 异步消息处理机制 让你深入理解 Looper.Handler.Message三者关系 android的消息处理机制(图+源码分析)——Looper,Handler,Message an ...

  10. sublime比较好用的插件

    emmet, markdown preview, package Control, SFTP, Anaconda