非完美C++ Singleton实现[2]

C# ScottPlot 5.0 WinForms 十字光标示例:轻松实现动态数值显示,你学会了吗? ScottPlot是一个免费、开源的.NET交互式绘图库,能够轻松地实现大型数据集的交互式显示。它支持多种图表类型,如折线图、柱状图、饼图、散点图等。ScottPlot的性能非常出色,即使是处理千万级别的数据也游刃有余。 阅读详情
4.解决多线程问题
上一篇实现的Singleton只能在单线程环境中使用,在多线程环境中会出现很多问题,看Instance()实现代码:
1 static Singleton&  Instance() {
2     if (0 == _instance) { //1

3         _instance = new Singleton(); //2
4          atexit(Destroy);
5 
    }
6     return *_instance; //3

7 }
考虑如下情况:线程一调用Instance(),进入//1,0 == _instance 返回true,线程一于是进入//2。这时候线程一被挂起,线程二开始执行,线程二调用Instance(),进入//1,发现0 == _instance 仍然返回true,线程二于是也进入//2,线程二继续执行到//3直到返回。这时候线程一被唤醒,继续从//2开始执行,这将会覆盖线程二创建的_instance,线程一继续执行到//3直到返回...

解决方法很简单,引入相关同步对象(synchronization object)就行了,例如在win32平台下可以如下实现:
synobj.h
 1  #ifndef SYNOBJ_H
 2 
#define SYNOBJ_H
 3 

 4 #include <windows.h>
 5 
 6  #define CLASS_UNCOPYABLE(classname) /
 7     private
: /
 8     classname(const classname&
); /
 9     classname& operator=(const classname&
);
10 

11 class  Mutex {
12 
    CLASS_UNCOPYABLE(Mutex)
13 public
:
14     Mutex() :_cs() { InitializeCriticalSection(&
_cs); }
15     ~Mutex() { DeleteCriticalSection(&
_cs); }
16     void lock() { EnterCriticalSection(&
_cs); }
17     void unlock() { LeaveCriticalSection(&
_cs); }
18 private
:
19 
    CRITICAL_SECTION _cs;
20 
};
21 

22 class  Lock {
23 
    CLASS_UNCOPYABLE(Lock)
24 public
:
25     explicit Lock(Mutex&
 cs) :_cs(cs) { _cs.lock(); }
26     ~
Lock() { _cs.unlock(); }
27 private
:
28     Mutex&
 _cs;
29 
};
30 

31 #endif/*SYNOBJ_H*/

有了同步对象很容易就能够写出如下代码:
singleton.h
 1  #ifndef SINGLETON_H
 2 
#define SINGLETON_H
 3 

 4 #include "synobj.h"
 5 
 6 class  Singleton {
 7 public
:
 8     static Singleton& Instance() { // Unique point of access

 9          Lock lock(_mutex);
10         if (0 ==
 _instance) {
11             _instance = new
 Singleton();
12             atexit(Destroy); // Register Destroy function

13          }
14         return *
_instance;
15 
    }
16     void
 DoSomething(){}
17 private
:
18     static void Destroy() { // Destroy the only instance

19         if ( _instance != 0  ) {
20 
            delete _instance;
21             _instance = 0
;
22 
        }
23 
    }
24     Singleton(){} // Prevent clients from creating a new Singleton

25     ~Singleton(){} // Prevent clients from deleting a Singleton
26     Singleton(const Singleton&); // Prevent clients from copying a Singleton
27     Singleton& operator=(const Singleton& );
28 private
:
29     static
 Mutex _mutex;
30     static Singleton *_instance; // The one and only instance

31  };
32 

33 #endif/*SINGLETON_H*/

singleton.cpp
1 #include "singleton.h"
2 
3  Mutex Singleton::_mutex;
4 Singleton* Singleton::_instance = 0;
现在的Singleton虽然多线程安全,性能却受到了影响。从Instance()中可以看到,实际上仅仅当0 == _instance为true时才需要Lock。你很容易就写出如下代码:
1 static Singleton&  Instance() {
2     if (0 ==
 _instance) {
3 
        Lock lock(_mutex);
4         _instance = new
 Singleton();
5 
        atexit(Destroy);
6 
    }
7     return *
_instance;
8 }
但是这样还是会产生竞争条件(race condition),一种广为人知的做法是使用所谓的Double-Checked Locking:
 1 static Singleton&  Instance() {
 2     if (0 ==
 _instance) {
 3 
        Lock lock(_mutex);
 4         if (0 ==
 _instance) {
 5             _instance = new
 Singleton();
 6 
            atexit(Destroy);
 7 
        }
 8 
    }
 9     return *
_instance;
10 }
Double-Checked Locking机制看起来像是一个完美的解决方案,但是在某些条件下仍然不行。简单的说,编译器为了效率可能会重排指令的执行顺序( compiler-based reorderings)。看这一行代码:

_instance = new  Singleton();

在编译器未优化的情况下顺序如下:
1.new operator分配适当的内存;
2.在分配的内存上构造Singleton对象;
3.内存地址赋值给_instance。


但是当编译器优化后执行顺序可能如下:
1.new operator分配适当的内存;
2.内存地址赋值给_instance;
3.在分配的内存上构造Singleton对象。


当编译器优化后,如果线程一执行到2后被挂起。线程二开始执行并发现0 == _instance为false,于是直接return,而这时Singleton对象可能还未构造完成,后果...

上面说的还只是单处理器的情况,在多处理器(multiprocessors)的情况下,超线程技术必然会混合执行指令,指令的执行顺序更无法保障。关于Double-Checked Locking的更详细的文章,请看:
The "Double-Checked Locking is Broken" Declaration

5.使用volatile关键字
为了说明问题,请先考虑如下代码:
 1 class MyThread : public  Thread {
 2 public
:
 3     virtual void
 run() {
 4         while (!
_stopped) {
 5             //do something

 6          }
 7 
    }
 8     void
 stop() {
 9         _stopped = true
;
10 
    }
11 private
:
12 
    bool _stopped;
13 
};
14 

15 ...
16 
17  MyThread thread;
18 thread.start();
上面用thread.start()开启了一个线程,该线程在while循环中检测bool标记_stopped,看是否该继续执行。如果想要结束这个线程,调用thread.stop()应该没问题。但是需要注意的是编译器很有可能对_stopped的存取进行优化。如果编译器发现_stopped被频繁存取(_stopped在while循环中),编译器可能会考虑将_stopped缓存到寄存器中,以后_stopped将会直接从寄存器存取。这时候如果某个线程调用了thread.stop(),对_stopped的修改将不会反映到寄存器中,thread将会永远循环下去...

为了防止编译器优化,用volatile关键字就OK了,volatile跟const的用法几乎一样,能用const的地方也都能用volatile。对Singleton来说,修改如下两处即可:
1 //singleton.h中
2 static Singleton * _instance;
3 //改为

4 static Singleton * volatile  _instance;
5 

6 //singleton.cpp中
7 Singleton* Singleton::_instance = 0 ;
8 //改为

9 Singleton* volatile Singleton::_instance = 0;


6.将Singleton泛化为模板
singleton.h
 1  #ifndef SINGLETON_H
 2 
#define SINGLETON_H
 3 

 4 #include "synobj.h"
 5 
 6 template<class T>
 7 class  Singleton {
 8 
    CLASS_UNCOPYABLE(Singleton)
 9 public
:
10     static T& Instance() { // Unique point of access

11         if (0 ==  _instance) {
12 
            Lock lock(_mutex);
13             if (0 ==
 _instance) {
14                 _instance = new
 T();
15 
                atexit(Destroy);
16 
            }
17 
        }
18         return *
_instance;
19 
    }
20 protected
:
21 
    Singleton(){}
22     ~
Singleton(){}
23 private
:
24     static void Destroy() { // Destroy the only instance

25         if ( _instance != 0  ) {
26 
            delete _instance;
27             _instance = 0
;
28 
        }
29 
    }
30     static
 Mutex _mutex;
31     static T * volatile _instance; // The one and only instance

32  };
33 

34 template<class T>
35 Mutex Singleton<T> ::_mutex;
36 

37 template<class T>
38 T * volatile Singleton<T>::_instance = 0 ;
39 

40 #endif/*SINGLETON_H*/

测试代码:
test.cpp
 1 #include "singleton.h"
 2 
 3 class A : public Singleton<A>  {
 4     friend class Singleton<A>
;
 5 protected
:
 6 
    A(){}
 7     ~
A(){}
 8 public
:
 9     void
 DoSomething(){}
10 
};
11 

12 int  main() {
13 

14     A &a =  A::Instance();
15 
    a.DoSomething();
16 

17     return 0 ;
18 }


7.Singleton的析构问题
到此Singleton已经算比较完善了,但是依然算不上完美,因为到现在只是解决了多线程问题,加入了模板支持,对于KDL problem(The Dead Reference Problem)依然没法解决,可以说在实现Singleton模式时,最大的问题就是多个有依赖关系的Singleton的析构顺序。虽然Modern C++ Design中给出了解决方案,但是Loki的实现太过复杂,在此就不详细说明了,有兴趣的可以看看Modern C++ Design,当然了,Loki库中用策略模式实现的Singleton也很不错!

终于搞定!Cloudflare Tunnel 报错 Error 1033,一次完整修复记录 最近遇到了 Cloudflare Tunnel Error 1033 报错的问题,本文记录了完整的排查和解决方案💡 阅读详情

相关推荐

LLaVA-1.5:多模态视觉问答新突破

LLaVA-1.5在多模态视觉问答(VQA)任务中取得了显著进展,主要得益于视觉-语言连接器的优化和特定任务数据集的增加。通过将连接器升级为两层MLP,模型在处理视觉与语言关系时表现更佳。同时,引入如OKVQA、OCRVQA等数据集,增强了模型在复杂视觉推理任务中的能力。在11个基准测试中,LLaVA-1.5超越了现有方法,展示了其强大的多模态能力。此外,模型通过提升输入图像分辨率、扩展语言模型和改进多语言能力,进一步提升了性能。尽管在短格式回答任务中初期表现不佳,但通过调整提示格式和微调,其性能显著提升

weixin_57060548的博客 945

Singleton

<br />#include <assert.h> template <typename T> class Singleton { protected: static T* ms_Singleton; public: Singleton( void ) { assert( !ms_Singleton ); ms_Singleton = static_cast<T*>(this); } ~Singleton( void ) { assert( m

Clean Sky 613

c语言实现语音检测vad_壹鸽技术科普·智能语音客服系统中的前端语音处理技术...

随着智能客服机器人产业的迅速发展,尤其是伴随着人工智能AI的风潮,预计在2020年我国智能客服市场将达到万亿级别。在智能客服机器人领域中对电话信道语音进行端点检测处理的应用正在不断扩展和深化。目前语音端点检测(Voice Active Detection,简称VAD)的主要任务是准确快速判决出待处理的语音信号有话还是无话,作为自动语音识别(Automatic Speech Recognition,...

weixin_36431018的博客 508

Android Converty问题解决方案

1.线程的使用    不建议使用的方法:       new Thread() {           public void run() {                           doPost(mContext, url, params, callback);         }}.start(); 建议使用: new Thread(new Runnable() {

张三的博客 5822

VSCode配置C/C++语言环境(小白专用保姆教程)(超细)

VSCode配置C/C++语言环境(小白专用保姆教程)(超细)

ITQWQ的博客 8229

高通Camx功能feature分析之十:高通NCS服务之注册和启动

本文介绍了高通NCS(非相机传感器)服务的注册与启动流程。NCS通过QMI接口访问陀螺仪等传感器,为相机EIS/AEC等功能提供辅助数据。文章详细解析了服务注册时的QueryCapabilites能力查询、RegisterService注册流程,以及通过StartNCSService启动服务线程的过程。重点包括:1)AF/EIS等节点通过NCSService创建传感器对象;2)注册时需校验传感器类型和接口;3)首次注册成功后会启动NCS服务线程;4)采样率变化时需重新配置会话。通过线程池管理实现传感器数据的

1262

Fortify Audit Workbench 笔记 Race Condition: Singleton Member Field 竞争条件:单例的成员字段...

Race Condition: Singleton Member Field 竞争条件:单例的成员字段 Abstract Servlet 成员字段可能允许一个用户查看其他用户的数据。 Explanation 许多 Servlet 开发人员都不了解 Servlet 为单例模式。 Servlet 只有一个实例,并通过使用和重复使用该单个实例来处理需要由不同线程同时处理的多个请求。 这种误解的共同后果...

马洪彪的流水账 353

innovus:InstPad、CellPad 、module padding、clock cell halo

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧?site_num指的是padding的大小以site宽度为单位。

m0_61544122的博客 2024

非完美C++ Singleton实现

非完美C++ Singleton实现[1] Singleton模式是一种非常简单的设计模式,这种模式很常用也很容易被滥用。当你设计应用程序的时候,经常会遇到某些对象在整个程序的生命周期应该仅有一个实例的情况,比如File System,Graphic System,Logging Utility,这时候就可以用到Singleton模式。Singleton模式在GOF中描述如下:       

kikikind的专栏 2254

FFDNet-pytorch版本代码训练教程

该文章为FFDNet-pytorch版本代码训练教程,抓紧订阅学习!尽早入门发论文哦!

假技术Po主 1507

Python爬虫实战:利用Playwright和反反爬技术高效抓取Bloomberg金融数据

用户行为分析(鼠标移动、点击模式)请求头检测IP频率限制WebSocket通信验证Canvas指纹识别我们的爬虫需要模拟人类行为,使用代理IP池,并合理控制请求频率。

2201_76125261的博客 1020

C++完美实现Singleton模式

Singleton模式是常用的设计模式之一,但是要实现一个真正实用的设计模式却也不是件容易的事情。 1. 标准的实现 class Singleton { public: static Singleton * Instance() { if( 0== _instance) { _instance = new Singleton; }

xu的blog 1602

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

C++ 非完美Singleton

非完美C++ Singleton实现[1] Singleton模式是一种非常简单的设计模式,这种模式很常用也很容易被滥用。当你设计应用程序的时候,经常会遇到某些对象在整个程序的生命周期应该仅有一个实例的情况,比如File System,Graphic System,Logging Utility,这时候就可以用到Singleton模式。Singleton模式在GOF中描述如下: Ens

wpc320的专栏 1058

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

非完美C++ Singleton实现[1]

非完美C++ Singleton实现[1] Singleton模式是一种非常简单的设计模式,这种模式很常用也很容易被滥用。当你设计应用程序的时候,经常会遇到某些对象在整个程序的生命周期应该仅有一个实例的情况,比如File System,Graphic System,Logging Utility,这时候就可以用到Singleton模式。Singleton模式在GOF中描述如下:       

benny5609的专栏 836

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

上一篇: 非完美C++ Singleton实现[1]
下一篇: Is your Singleton Broken?
olncy
博客等级 码龄20年 30粉丝 37原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值