• Singleton之C++部分


    采用静态或者全局变量的实现方案
    由于C++不能保证静态或者全局对象的构造函数的调用顺序以及析构顺序。所以如果程序中有多个用此方法实现的Singleton类,它们之间又有某种构造依赖关系和析构依赖关系,就会造成灾难性的后果。所以,只有当肯定不会有构造和析构依赖关系的情况下,这种实现才是合适的。

    > 优点 实现简单,多线程下安全
    > 缺点 如果有多个Singleton对象的创建顺序有依赖时,千万别用;不是lazy loading,有些浪费。
    Meyers Singleton来控制构造顺序,但是不能控制析构顺序
    Scott Meyer在<<Effective C++>>3rd Item4中提出了一个解决方案,当将non-local static变量移动到静态方法中成为local static变量的时候。C++保证当第一次静态方法被调用的时候,才会创建该静态变量。但是这里有一个疑问,创建顺序能够被控制了,可是析构顺序呢?我们只知道进程结束的时候,local static 变量会被析构,而且按照创建顺序的相反顺序进行。如果几个Singleton类的析构函数之间也有依赖关系,并且这种依赖顺序关系和LIFO顺序冲突,就会造成dead-reference问题。

    > 优点 实现简单;用的时候才创建,比较节省。
    > 缺点 多线程下不安全;如果有多个Singleton对象的析构顺序有依赖时,要小心
    DCLP 98标准下是不可靠的,0x标准下是可靠的
    DCLP 就是 Double-checked locking pattern.用于在多线程环境下保证只创建Singleton对象。第一次check不用加锁,但是第二次check和创建对象必须加锁。还要注意编译器可能会优化代码,导致DCLP模式失效。因此要使用volatile 修饰T* pInstance变量。先看一下 DCLP的实现代码:


    class Singleton {
    public:
    static Singleton* instance() {
    if (pInstance == 0) {
    Lock lock;
    if (pInstance == 0) {
    pInstance = new Singleton;
    }
    }
    return pInstance;
    }
    private:
    static Singleton * volatile pInstance;
    Singleton(){
    }
    };


    在c++98标准下,这是不可靠的。原因有三点:

    一,执行顺序得不到保证。编译器会优化代码,从而改变执行顺序。

    pInstance = new Singleton; 这个语句会分成三步完成:

    1.分配内存,

    2.在已经分配的内存上调用构造函数创建对象,

    3.将对象赋值给指针pInstance.

    但是这个顺序很可能会被改变为1,3,2。如果A线程在1,3执行完后,B线程执行第一个条件判断if(pInstance ==0),此时锁不能起到保护作用。B线程会认为pInstance已经指向有效对象,可以去使用了。嘿嘿,灾难发生。主要原因是C++98标准中没有包含多线程,只假定是单线程,编译器的优化行为无视多线程环境,因此产生的优化代码可能会被去掉你的代码或者改变执行顺序。我们没有办法在98标准的采用标准c++语言来解决这个问题,只能采用平台相关的多线程API和与之兼容的编译器来解决。因此,从本质上来说,基于98标准,此问题无解。

    二,volatile对于执行顺序也没有帮助。

    三,多处理器的系统,B处理器看到变量值的顺序可能和A处理器写变量值的顺序不一致。

    详细解释请参考Scott Meyers and Andrei Alexandrescu的论文:http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf

    技术总是发展的,2011标准的出台给DCLP带来了曙光,真的还能用么,拭目以待吧. 让我们先下个结论:

    > 缺点 98标准下不可用
    > 优点 让我们接受教育,包括Andrei和Scott meyer都犯过错。
    Andrei说4.11 scott meyer阐述了0x标准下,由于有了线程概念,内存模型,sequence point被sequenced before 和 happens before取代, 有了atomic等等,DCLP又可以复活了。 http://cppandbeyond.com/2011/04/11/session-announcement-the-c0x-memory-model-and-why-you-care/ 具体谈了什么呢?我还没有找到相关的文档。

    简单锁
    于是又回到老土的锁方案,其实注意一下调用,还是能够提高效率的。


    class Singleton {
    public:
    static Singleton* instance() {
    Lock lock;
    if (pInstance == 0) {
    pInstance = new Singleton;
    }
    return pInstance;
    }
    private:
    static Singleton * volatile pInstance;
    Singleton(){
    }
    };


    客户调用时,在每个线程的开头都获得Singleton* p = Singleton::instance();以后就一直使用这个p变量,应该说还是能有效的降低同步的机会。避免频繁调用Singleton::instance()->就好。

    > 优点 实现简单,线程安全
    > 缺点 客户需要意识到,并且遵守少调用的原则。

    程序开始之前创建Singleton对象
    严格来说,这是个策略。将要初始化的放到程序最开始初始化。在这个策略下,以上几种方案都是可以的,包括DCLP。因为总是在单线程中创建对象。
    ————————————————
    版权声明:本文为CSDN博主「csfreebird」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/csfreebird/article/details/6429946

  • 相关阅读:
    迷宫最短路问题
    回溯算法
    解题报告:poj1321 棋盘问题
    矩阵、分数、点、线类
    判断图像中有多少行文本(开发中)
    图形-回行扫描函数
    贝叶斯分类器
    js解析数学运算公式
    用postcss给less加上webkit前缀
    node创建文件夹
  • 原文地址:https://www.cnblogs.com/dongzhiquan/p/16110275.html
Copyright © 2020-2023  润新知